spring aop
疑问
aspectJ、cglib、asm分别是什么?有什么联系?
aspectJ和cglib是代理框架,asm是字节码框架。
cglib用到了asm。
spring aop或者其他的aop,有多个before、around、after时,需要定义先后顺序吗?如何实现?
order或者实现Order接口,定义顺序数值。值越小越先执行(越在同心圆外圈)。
spring的aspectJ,是spring aop代理?用的动态代理,还是cglib?
用的动态代理。
spring只用到了aspectJ的注解,没用到aspectJ的各种织入。
稍稍了解下,cglib怎么修改字节码。
小结
- spring aop
- 基于动态代理实现,在容器启动的时候生成代理实例(cglib起什么作用?)
- 作用于容器中的所有bean
- 提供了对asjectJ的支持(怎么理解)?
- 沿用(使用)了AsjectJ @AsjectJ、@Pointcut等注解,但注解的实现是spring自己实现的一套
- AspectJ是AOP编程的完全解决方案。
- 静态织入,通过修改代码实现
- 织入时机包括
- 编译期织入 compile-time weaving
- 编译后织入 post-compile weaving 已经生成.class文件了
- 加载时织入 load-time weaving 在加载类的时候织入,方式包括自定义加载类,或在JVM启动时指定AspectJ提供的agent
-javaagent:xxx/xxx/aspectjweaver.jar
Spring aop 源码
配置DefaultAdvisorAutoProxyCreator
的方式,使所有的advisor
都生效。
DefaultAdvisorAutoProxyCreator
继承了BeanPostProcessor
,在每个bean实例化后被调用,doCreateBean..initializeBean..applyBeanPostProcessorsAfterInitialization(..)..
调用每个BeanPostProcessor
的postProcessAfterInitialization
。
在DefaultAdvisorAutoProxyCreator
(父类AbstractAutoProxyCreator
)的postProcessAfterInitialization
中,生成bean的代理,将这个代理返回,放在容器中。
1 | // AbstractAutowireCapableBeanFactory.java |
DefaultAopProxyFactory
有两种默认实现方式,JDK动态代理和cglib代理。
设置optimize、proxyTargetClass或当前类没有实现接口是,使用cglib代理(如果类本身是接口、或是代理类,也使用jdk动态代理)。
其他情况下用JDK动态代理。
一般情况下,指定proxyTargetClass来使用cglib,指定一个或多个接口来使用JDK代理。jdk代理
原理
基于反射机制。
JDK利用反射机制生成一个实现代理接口的匿名类,然后重写方法,实现方法的增强。
生成匿名类很快,但后续调用栈比较深,执行速度会相对慢一些。
流程:
- 为接口创建代理类的字节码文件(重写的方法里调用了InvokeHandler的invoke方法)
- 使用ClassLoader加载类文件进JVM
附录1给出了一个反编译出来的匿名类的代码。
代码
1 | // JdkDynamicAopProxy.java |
生成的JDK动态代理实例如图中所式示
- 是一个Proxy类型的实例,有一个名称为h的成员变量,h为JdkDynamicAopProxy类型(父类未InvocationHandler),这里都和jdk动态代理是一致的
- JdkDynamicAopProxy主要有advised变量,内部有advisors(理解为增强方法)和targetSource(封装了被代理实例)
- 调用proxy时,实际上是调用了InvocationHandler的invoke方法,简化了说,先执行advisors的方法,再反射调用
method.invoke(target, args)
,或是链式执行。
cglib代理
原理
cglib基于继承机制,继承被代理类,通过字节码重写,增强父类的方法。
底层基于asm第三方框架,加载被代理类的class文件,修改字节码生成子类。
可以用于接口,也可以用于类。
设计模式
代理模式
责任链模式
切面执行顺序
更确切的说,是advisor的执行顺序。
每个@Aspect
类,根据@Before
、@After
可生成相应个数的advisor(也有order值)。
可为Aspect指定order,order值越小,越先执行。
内容来自:基于AspectJ的Spring AOP Advice执行顺序
When two pieces of advice defined in different aspects both need to run at the same join point, unless you specify otherwise the order of execution is undefined. You can control the order of execution by specifying precedence. This is done in the normal Spring way by either implementing the org.springframework.core.Ordered interface in the aspect class or annotating it with the Order annotation. Given two aspects, the aspect returning the lower value from Ordered.getValue() (or the annotation value) has the higher precedence.
上面的内容简单的说就是,当对于同一个Join Point有两个Advice定义在不同的Aspect中的时候,他们的执行顺序是根据Aspect类的@Order注解的值,或者通过实现Order并重写getValue方法的值来决定的.同时,Order的值越小,优先级越高.
When two pieces of advice defined in the same aspect both need to run at the same join point, the ordering is undefined
当同一个Aspect中对同一个Join Point有两个Advice的话,这两个Advice的顺序是不固定的.
AOP应用
全局日志、异常、事务管理等
参考文献
- aop源码分析
- AspectJ使用介绍
- spring aop 使用介绍
- Spring AOP,AspectJ, CGLIB
- JDK动态代理实现原理
- 基于AspectJ的Spring AOP Advice执行顺序
附录
反编译出的Proxy0
成员变量是原接口的方法,在重写的相应的方法中被调用。
例如Method m1是原接口实例的equals方法,Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"))
,在新的equals方法中被反射调用,super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
。
1 | // |
补充
spring aop代理每个bean的流程小结
- 每个advisor是一个bean,连接了一个advice和一个pattern
- 在DefaultAdvisorAutoProxyCreator这个BeanPostProcessor中,warpIfNessary
- 在容器中查询出所有advisor,常用的是基于正则的advisor
RegexpMethodPointcutAdvisor
(还会对不同类型的advisor做一些处理,比如用DefaultPointcutAdvisor
封装MethodInterceptor
类型的advisor) - 将bean与advisors放入proxyFactory,创建代理对象
- 在容器中查询出所有advisor,常用的是基于正则的advisor
这里缓存着@Aspect注解的类。它又是怎么生成的呢?遍历beanDefinitions,取className对应的Class,看有没有注解
bpp判断要不要代理:先获取候选advisor,对每个Advisor判断是不是当前bean需要的(通过正则等),如果都没有需要的Advisor,则不代理;否则生成代理。
获得xml配置的advisor
比较古老的通过配置文件定义切面的方式。
可以看出,每个advisor连接了一个advice和一个pattern。advice是方法维度的,例如是一个MethodBeforeAdvice
。
这里DefaultAdvisorAutoProxyCreator
是一个BeanPostProcessor
,在bean实例化后的回调方法中,触发代理的创建。
ps: DefaultAdvisorAutoProxyCreator
与下面@Aspect
要用的AnnotationAwareAspectJAutoProxyCreator
同宗啊。
default方式的查找advisors,是直接通过获取Advisor类型的bean实现的。
从XML配置中可以看到,advisor实现了RegexpMethodPointcutAdvisor接口,是Advisor类型的。
获得@Aspect
注解的advisor
那通过@Aspect
注解的切面是怎么组装的呢?
首先,@Aspect
注解的类需要在xml中配置成bean、或者加@Component
注解等,能够托管在IOC容器中。
其次,需要开启@Aspect
注解的自动解析。
AopNamespaceHandler
会注册一个AspectJAutoProxyBeanDefinitionParser
。【在什么时候使用到这个parser?】parser在parse方法中,注册了AnnotationAwareAspectJAutoProxyCreator.class
(是SmartInstantiationAwareBeanPostProcessor
的一个实现),就是这个类起着寻找并创建advisor的重要作用。从截图的2步骤开始。
1 | <!--开启 @AspectJ 配置--> |
寻找并创建advisor的流程:
- 从beanFactory中取出所有的beanNames
- 对每一个beanName判断是否为aspect,其中一种判断就是看是否有Aspect注解,如截图步骤4.
- 如果是aspect,由
ReflectiveAspectJAdvisorFactory
生成类中的各advisors。
几种proxyCreator
第四个没见过
beanName,就是在xml文件中直接写出哪些beanName是Advice,同时bean会实现MethodBeforeAdvice
等接口。可能是比较古老的方式。