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”/>)。
性能区别:
-
CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
-
在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。
各自局限:
-
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。
-
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动态代理是基于接口而不是继承实现的。
反射的实现原理:
- 反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
- 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
- 反射也是考虑了线程安全的,放心使用;
- 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
- 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
- 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
- 调度反射方法,最终是由jvm执行invoke0()执行;
***spring aop执行顺序


1741

被折叠的 条评论
为什么被折叠?



