Spring-AOP

AOP 入口

通过扫描注解@EnableAspectJAutoProxy(proxyTargetClass =
true,exposeProxy = true)注册了AOP 入口类,具体看看注解里面的
@Import(AspectJAutoProxyRegistrar.class)
在这里插入图片描述
proxyTargetClass
true
1、目标对象实现了接口– 使用CGLIB 代理机制
2、目标对象没有接口(只有实现类) – 使用CGLIB 代理机制
false
1、目标对象实现了接口– 使用JDK 动态代理机制(代理所有实现了的接口)
2、目标对象没有接口(只有实现类) – 使用CGLIB 代理机制

exposeProxy
该属性设置代理对象是否需要暴露,是否需要把代理对象设置到ThreadLocal 中。

是否生成代理

当一个bean 实例化完成后,会判断该bean 是否生成代理,AOP 的入口
在这里插入图片描述
在这里插入图片描述
这是一个BeanPostProcessor 接口的运用,initializeBean 方法我们都知道是一个
bean 实例化完成后做的操作,而这个代理实例生成也是在bean 实例化完成后做的操作
在这里插入图片描述
这个方法就是判断当前bean 是否有切面advisor,如果有切面就会走到createProxy
方法,生成代理对象然后返回。

寻找切面

在这里插入图片描述
1、从spring 中找所有的切面
2、找到拦截当前bean 的切面

从spring 中找所有切面
先找到所有的beanDefinition 对象对应的beanName,
拿到对应的Class 对象,判断该类上面是否有 @Aspect 注解,如果有则是我们要找的,
循环该Class 里面的除了 @PointCut 注解的方法,找到方法上面的Around.class,
Before.class, After.class, AfterReturning.class, AfterThrowing.class
注解,并且把注解里面的信息,比如表达式,argNames,注解类型等信息封装成 对象
AspectJAnnotation
,然后创建 pointCut 对象,把注解对象中的表达式设置到pointCut
对象中,然后就是创建 Advice 对象,根据不同的注解类型创建出不同的Advice 对象,对象
如下:AspectJAroundAdvice,AspectJAfterAdvice,
AspectJAfterThrowingAdvice,AspectJMethodBeforeAdvice,
AspectJAfterReturningAdvice 最终把注解对应的Advice 对象和pointCut 对象封装成
Advisor 对象

找到拦截当前bean 的切面
从收集到的所有切面中,每一个切面都会有pointCut 来进行模块匹配,其实这个过程就
是一个匹配过程,看看pointCut 表达式中的内容是否包含了当前bean,如果包含了,那么这
个bean 就有切面,就会生成代理。

代理类的调用

在这里插入图片描述
createProxy 方法里面,这个方法就会生成bean的代理实例。

创建代理的过程:
1、创建代理工厂对象ProxyFactory
2、切面对象重新包装,会把自定义的MethodInterceptor 类型的类包装成Advisor
切面类并加入到代理工厂中
3、根据proxyTargetClass 参数和是否实现接口来判断是采用jdk 代理还是cglib 代

4、创建代理对象,并且把代理工厂对象传递到jdk 和cglib 中,注意这里的代理对象和
jdk 类和cglib 类是一一对应的。

代理实例的调用
上面我们已经创建出来了代理对象了,现在是拿到代理对象调用,以jdk 动态代理为例,
cglib 是一样的调用逻辑。
当发生代理对象调用时,肯定会调用到实现了invocationHandler 接口的类,这个类就
是:JdkDynamicAopProxy
必定会调用到该类的invoke 方法。
在这里插入图片描述

JDK动态代理和CGLIB的区别

JDK动态代理
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。核心是实现InvocationHandler接口,使用invoke()方法进行面向切面的处理,调用相应的通知。

CGLiB动态代理
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。核心是实现MethodInterceptor接口,使用intercept()方法进行面向切面的处理,调用相应的通知。

spring中代理的选用
1, 当Bean实现接口时,Spring就会用JDK的动态代理。
2, 当Bean没有实现接口时,Spring使用CGlib是实现。
3, 可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)。

性能区别:

  1. CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

  2. 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。

各自局限:

  1. JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。

  2. cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

类型机制回调方式适合场景效率
jdk委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法反射目标类是接口类效率瓶颈在于反射调用稍慢
cglib继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类执行原始逻辑通过FastClass方法索引调用非接口类,非final类,非final方法第一次调用要生成多个class对象较jdk方式慢,如果方法过多switch case过多其效率还需测试

jdk代理是基于接口的

因为jdk动态代理生成的class文件已经继承了Proxy,而java是单继承的,所以是基于jdk动态代理是基于接口的。
请看代码使用jdk动态代理生成class文件
在这里插入图片描述
可以明显的看到class文件已经继承了Proxy,所以不能继承目标对象,只能实现目标对象,所以jdk动态代理是基于接口而不是继承实现的。

反射的实现原理:

  1. 反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
  2. 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
  3. 反射也是考虑了线程安全的,放心使用;
  4. 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
  5. 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
  6. 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
  7. 调度反射方法,最终是由jvm执行invoke0()执行;

***spring aop执行顺序

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值