Spring-AOP综述

发布时间:2024年01月02日

文章迁移自语雀。

今天下雪了, 完成了spring aop的应用学习, 学到了以前很多忽略的知识点,也写完了各种试验.
今天给程程分享了很多了随州下雪的场景,写了一二三, ?程程, i miss u 等字体,在雪地上写给程程看, 还拍摄了照片和视频给她, 上午的时候,还做了一个雪人, 哈哈哈, 她说好丑.做雪人真心冷.
今天马超分享了一个赚钱的app叫什么东方体育, 让我试试, 有5天的免费时间,应该可以撸80多块钱左右.
2020年2月15日16:23:49


在项目中xml用来描述元数据, 属于很重的配置信息,因为有约束,不正确的话有提示.
java config也是配置文件, 有一些结构上的约束.

自定义注解
查看注解的级别是source在编译之后是否还存在...
获取注解内容
source级别时候,在class文件中不存在的
class级别只在class上有,在运行时候就没有了!!
代码如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
? ?public String value();
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
? ?public String value();
}

@Entity("user")
public class User {
? ?@Column("id")
? ?private String id;
? ?@Column("name")
? ?private String name;
? ?public String getId() {
? ? ? ?return id;
? ?}
? ?public void setId(String id) {
? ? ? ?this.id = id;
? ?}
? ?public String getName() {
? ? ? ?return name;
? ?}
? ?public void setName(String name) {
? ? ? ?this.name = name;
? ?}
}

//测试类
public class ChengCheng {
? ?public static void main(String[] args) {
? ? ? ?User user = new User();
? ? ? ?user.setId("18");
? ? ? ?user.setName("chengcheng");
? ? ? ?Class<? extends User> userClass = user.getClass();
? ? ? ?Entity entity = userClass.getDeclaredAnnotation(Entity.class);
? ? ? ?System.out.println(entity.value());
? ? ? ?Field[] fields = userClass.getDeclaredFields();
? ? ? ?for (Field field : fields) {
? ? ? ? ? ?Column annotation = field.getAnnotation(Column.class);
? ? ? ? ? ?System.out.println(annotation.value());
? ? ? ?}
? ?}
}






AOP:
横切性问题,不会影响主业务逻辑
日志记录
权限验证
效率检查
事务管理
...

aop概念
与OOP对比,面向切面,传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不大,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP是处理一些横切性问题,AOP的编程思想就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。使代码的重用性和开发效率更高。

springAop的底层技术

JDK动态代理

CGLIB代理
?

编译时期的织入还是运行时期的织入?

运行时期织入

运行时期织入

初始化时期织入还是获取对象时期织入?

初始化时期织入

初始化时期织入



springAop和AspectJ的关系
Aop是一种概念
springAop、AspectJ都是Aop的实现,SpringAop有自己的语法,但是语法复杂,所以SpringAop借助了AspectJ的注解,但是底层实现还是自己的

aspect:切面, 就是下面的概念的集合.是他们的载体.
join point:连接点 代表一个方法的执行
目标对象中的方法
advice:通知 什么时候通知?通知到哪里去?
通知类型:before,after,after throw...
pointcut:切点 连接点目标对象中的方法
切点就是连接点的集合.
introduction:
target:目标对象
weaving:织入
方法增强的过程
aop proxy:代理对象, 包含了原始对象的代码和我们增强后的代码的那个对象

advice通知类型:
Before 连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常
After 连接点正常执行之后,执行过程中正常执行返回退出,非异常退出
After throwing 执行抛出异常的时候
After (finally) 无论连接点是正常退出还是异常退出,都会执行
Around advice: 围绕连接点执行,例如方法调用。这是最有用的切面方式。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。

aspectj与spring关系
@AspectJ ?支持
aop和spring aop有什么关系?
aop是一种思想,spring aop是一种实现.AspectJ也是一种实现.

aspectj表达式
表达式:
execution:
within:
两者区别:execution能更加的精确,更加通用
within最小的定义范围是类
args,只是和参数有关
@Annotation ?注解,方法上的注解.
this:表示的是代理对象的类型匹配
jdk的代理中,代理对象不等于原对象
cglig中,代理对象等于原对象
target:目标对象
目标对象永远不会变.


@EnableAspectJAutoProxy(proxyTargetClass=true)
true使用cglib代理,flase默认使用jdk的代理.
代理
继承 聚合接口 动态代理
因为java是单继承,所以是基于接口实现的.
extens Proxy implements ?XXXService


下面是使用around进行做环绕通知.
public void around(ProceedingJoinPoint aop){
}
ProceedingJoinPoint 继承自JoinPoint

有两个实现类, 不过我们一般看接口就可以了.

gProceedingJoinPoint(org.aspectilang)

oJoinPointlmpl(org.aspecti.runtect)

MethodlnvocationproceedingloinPoint(org.springframe

image.png



此类的方法有:
Proceedingjoinpoint 和JoinPoint的区别:
Proceedingjoinpoint 继承了JoinPoint,proceed()这个是aop代理链执行的方法。并扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。
JoinPoint的方法
1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;?
2.Signature getSignature() :获取连接点的方法签名对象;?
3.java.lang.Object getTarget() :获取连接点所在的目标对象;?
4.java.lang.Object getThis() :获取代理对象本身;
proceed()有重载,有个带参数的方法,可以修改目标方法的的参数

joinPoint.process();
joinPoint.process(参数...);

同时还可以改变参数.
getArgs();

各种连接点joinPoint的意义:
1execution
用于匹配方法执行?join points连接点,最小粒度方法,在aop中主要使用。
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下
modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
name-pattern:方法名类型,如buisinessService();
param-pattern:方法的参数类型,如java.lang.String;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
example:
@Pointcut("execution(* com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和类的任意方法
@Pointcut("execution(public * com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和类的public方法
@Pointcut("execution(public * com.chenss.dao.*.*())")//匹配com.chenss.dao包下的任意接口和类的public 无方法参数的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")//匹配com.chenss.dao包下的任意接口和类的第一个参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(public * *(..))")//匹配任意的public方法
@Pointcut("execution(* te*(..))")//匹配任意的以te开头的方法
@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")//匹配com.chenss.dao.IndexDao接口中任意的方法
@Pointcut("execution(* com.chenss.dao..*.*(..))")//匹配com.chenss.dao包及其子包中任意的方法

关于这个表达式的详细写法,可以脑补也可以参考官网很容易的,可以作为一个看spring官网文档的入门,打破你害怕看官方文档的心理,其实你会发觉官方文档也是很容易的
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的信息,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的
1within
?表达式的最小粒度为类
// ------------
// within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等
@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法
@Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法

1args
?args表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关


/**
* args同execution不同的地方在于:
* args匹配的是运行时传递给方法的参数类型
* execution(* *(java.io.Serializable))匹配的是方法在声明时指定的方法参数类型。
*/
@Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配
@Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classified
1this JDK代理时,指向接口和代理类proxy,cglib代理时 指向接口和子类(不使用proxy)
2target 指向接口和子类
/**
* 此处需要注意的是,如果配置设置proxyTargetClass=false,或默认为false,则是用JDK代理,否则使用的是CGLIB代理
* JDK代理的实现方式是基于接口实现,代理类继承Proxy,实现接口。
* 而CGLIB继承被代理的类来实现。
* 所以使用target会保证目标不变,关联对象不会受到这个设置的影响。
* 但是使用this对象时,会根据该选项的设置,判断是否能找到对象。
*/
@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类
@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个
@Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法
@Pointcut("@within(com.chenss.anno.Chenss)")//等同于@targ

这个比较难.......
proxy模式里面有两个重要的术语
proxy Class
target Class
CGLIB和JDK有区别 JDK是基于接口 cglib是基于继承所有this可以在cglib作用



1@annotation
这个很简单........
作用方法级别

上述所有表达式都有@ 比如@Target(里面是一个注解类xx,表示所有加了xx注解的类,和包名无关)

注意:上述所有的表达式可以混合使用,|| && !

@Pointcut("@annotation(com.chenss.anno.Chenss)")//匹配带有com.chenss.anno.Chenss注解的方法

@DeclareParents 是一个不常用的注解, 主要做类的接口增强的.

区别:
aspectj静态织入 ?编译后就织入
aop动态织入 ? ? ?运行时才织入
如果object不被spring 管理,推荐使用aspectj,否则使用spring aop
有时间多看文档.

附上完整的示例代码:
?

package com.onyx.aop.service;
import com.onyx.ann.Entity;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserService {
? ?@Entity("123")
? ?public void login() {
? ? ? ?System.out.println("this is UserService login method");
? ?}
? ?public void register(String name) {
? ? ? ?System.out.println("this is UserService register method~~~"+name);
? ?}
}
package com.onyx.aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(value = "com.onyx.aop.**")
//开启aspectj的代理,true 是使用aspect代码, false是用jdk代理
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class AppConfig {
}
package com.onyx.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserAspect {
? ?@Pointcut("execution(* com.onyx.aop.service.UserService.*(..))")
? ?public void pointcutExe() {
? ?}
? ?//within只能到类级别的
? ?@Pointcut("within(com.onyx.aop.service.UserService)")
? ?public void pointcutWithin() {
? ?}
? ?//根据参数进行匹配.
? ?@Pointcut("args(java.lang.String)")
? ?public void pointcutArg() {
? ?}
? ?//这里放置的注解是放在方法上的.不是放在类上的.
? ?@Pointcut("@annotation(com.onyx.ann.Entity)")
? ?public void pointcutAnn() {
? ?}
? ?@Around(value = "pointcutArg()")
? ?public void around(ProceedingJoinPoint proceedingJoinPoint) {
? ? ? ?try {
? ? ? ? ? ?System.out.println("around before");
? ? ? ? ? ?//proceedingJoinPoint.proceed();
? ? ? ? ? ?//这里可以修改参数
? ? ? ? ? ?Object[] args = proceedingJoinPoint.getArgs();
? ? ? ? ? ?for (int i = 0; i < args.length; i++) {
? ? ? ? ? ? ? ?Object arg = args[i];
? ? ? ? ? ? ? ?args[i] = "change the args" + arg;
? ? ? ? ? ?}
? ? ? ? ? ?proceedingJoinPoint.proceed(args);
? ? ? ?} catch (Throwable throwable) {
? ? ? ? ? ?throwable.printStackTrace();
? ? ? ?}
? ? ? ?System.out.println("around after");
? ?}
? ?@Before(value = "pointcutAnn()")
? ?public void before() {
? ? ? ?System.out.println("aspect before method");
? ?}
}
package com.onyx.aop;
import com.onyx.aop.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ChengCheng {
? ?public static void main(String[] args) {
? ? ? ?AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
? ? ? ?UserService bean = context.getBean(UserService.class);
? ? ? ?bean.login();
? ? ? ?bean.register("123");
? ?}
}


?

1 人点赞

  • 长河

1

文章来源:https://blog.csdn.net/u010398771/article/details/134005997
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。