SpringAOP的使用和源码分析
前言
现在来看(Aspect-oriented Programming) 面向切面编程aop,是和oop(Object-oriented Programming)是对编程结果的不同的思考的方向。在面向对象中,形成组合的组件是类(class)。而在面向切面,形成组合的组件是切面(Aspect),切面能够对关注的地方模块化,横切多种不同的类型和对象(比如事务管理)。而这个所谓的“关注的地方”,也可以在AOP中称为跨领域关注点。
Spring提供 配置文件 和 @AspectJ注解 方式编写AOP,两种方式都支持各种的 Advice(通知),如果使用@AspectJ注解,仍然会通过Spring的进行织入。
AOP中关键字的概念
1.Aspect
切面: 对多种不同的类的横切关注点,这个关注点被模块化为一个类,这个类就叫做切面(切面类),通过在普通类上标注 @Aspect
注解来实现。
2.Join point
连接点:程序执行过程中能够执行通知(Advice)的点。
3.Advice
通知:通知指的是切面在指定的连接点处执行的操作,不同的通知类型包括:“around”, “before” and “after”
4.Pointcut
切点:切点会匹配通知所要织入的一个或者多个连接点,通知是与切点向关联的,并且在任何匹配到切点的地方执行在连接点上的操作。
5.Target object
目标对象:一个【被一个或多个切面进行通知】的对象叫做目标对象,也叫代理对象。
6.AOP proxy
AOP代理:在Spring中,一个是JDK动态代理,一个是 CGLIB 代理。
7.Weaving
织入:织入是将切面应用到目标对象来创建的代理对象过程
使用注解方式进行AOP的配置和使用
1 | /** |
AOP代码原理分析
通过以上实践,可以看到如果需要使用Spring的AOP功能,需要先使用 @EnableAspectJAutoProxy
注解标注在配置类中,使 @Aspect
生效为一个切面类。
1. @EnableAspectJAutoProxy 开启springAOP支持
1 | //标注在类上 (ElementType.TYPE) |
可以看到这里使用了Import注解,将AspectJAutoProxyRegistrar
引入到配置中。引入后,会通过继承自ImportBeanDefinitionRegistrar
接口的方法 registerBeanDefinitions
做后续处理。
1 |
|
最后通过 registerOrEscalateApcAsRequired
方法将 cls
也就是 AnnotationAwareAspectJAutoProxyCreator
注册到容器中,名称为 internalAutoProxyCreator
。
1 |
|
经过上面的分析能够了解通过使用 @EnableAspectJAutoProxy
这个注解,能够将AnnotationAwareAspectJAutoProxyCreator
引入到容器中,从而达到解析,使用AOP。
那么 AnnotationAwareAspectJAutoProxyCreator 是如何工作的呢?为何引入这个类,才能使用Spring的AOP功能呢?
2. @AnnotationAwareAspectJAutoProxyCreator
类结构初步分析
对AnnotationAwareAspectJAutoProxyCreator
生成类结构图,可以看到它继承自有两个顶级接口,分别是 BeanPostProcesser
和 Aware
接口。
Aware 接口主要用于标记超级接口,用于指示bean有资格通过回调样式方法由Spring容器通知特定框架对象。
BeanPostProcesser 可以通过实现 BeanPostProcesser的两个后置处理器来实现对Bean的前后置处理:1.
postProcessBeforeInitialization(Object bean, String beanName)
2.postProcessAfterInitialization(Object bean, String beanName)
分别针对初始化前后的拦截处理。
a. AbstractAutoProxyCreator
在通过上面图片中所看到的 AbstractAutoProxyCreator
继承了 ProxyProcessorSupport
类
继承类/接口 | 作用 | 需要实现方法 |
---|---|---|
ProxyConfig类 | 用于创建代理的配置的便利超类,保证所有的代理创建类有一致的属性 | |
Ordered接口 | 表示继承类是有序的 | |
BeanClassLoaderAware接口 | 使用指定的classLoader来加载Bean | setBeanClassLoader(ClassLoader classLoader) |
AopInfrastructureBean接口 | 标示该Bean是作为Spring AOP的基础组件进行使用,因此不会被代理,即使被PointCut指定到也不会被代理到 | |
BeanFactoryAware | 使用指定的 BeanFactory 来实例化Bean | void setBeanFactory(BeanFactory beanFactory) |
b. SmartInstantiationAwareBeanPostProcessor
AbstractAutoProxyCreator -> 实现自 SmartInstantiationAwareBeanPostProcessor
-> 继承自 InstantiationAwareBeanPostProcessor
-> 继承自 BeanPostProcessor
.
- InstantiationAwareBeanPostProcessor
在这个接口中,不仅仅有继承于 BeanPostProcessor
的 postProcessBeforeInitialization(Object bean, String beanName)
和 postProcessAfterInitialization(Object bean, String beanName)
两个Bean初始化前后的处理方法,本身还定义了 postProcessProperties
和 postProcessPropertyValues
方法。作用是在针对Bean的初始化后对属性进行后置处理。
- SmartInstantiationAwareBeanPostProcessor
在这个接口中定义两个方法,分别为 default Class<?> predictBeanType(Class<?> beanClass, String beanName)
用来预测使用BeanPostProcessor的postProcessBeforeInstantiation方法的返回类型,默认为null。default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
用来决定使用哪个候选的构造器。
此时,相比已经在前面了解到什么是AOP,同时可以在前面的类结构分析中得知了AnnotationAwareAspectJAutoProxyCreator 继承自
InstantiationAwareBeanPostProcessor
从而实现了对Bean的前后处理。所以下面来着重找和 对Bean 前后置处理相关的方法。
AbstractAutoProxyCreator类源码分析
在 AbstractAutoProxyCreator
作为 AnnotationAwareAspectJAutoProxyCreator
的父类,起到了非常重要的作用。
在 AbstractAutoProxyCreator
中,实现了 BeanPostProcessor
的初始化前后处理方法,用来针对每次初始化Bean前后对切面的识别判断和对符合条件的Bean进行通知的代理。
1 | // postProcessBeforeInitialization ,BeanPostProcessor的实现方法。 |
在postProcessBeforeInstantiation
实现类中,具体流程大致为
- 如果是
Aspect
切面类,则将类添加到advisedBeans
中等待进行Advice
的组装 - 如果不是切面类,则会先去判断是否自定义一个
customTargetSourceCreators
数组- 如果定义了,则组装成自定义的代理类进行返回。
1.是否为AspectJ配置类
如果是AspectJ定义的切面类,或者由Advice、Pointcut、Advisor、AopInfrastructureBean实现类,则添加到advisedBeans中,并制定值为FLASE。
在 AnnotationAwareAspectJAutoProxyCreator
中存在 的isInfrastructureClass
实现方法。
1 |
|
2.进行代理
真正的代理是发生在实现的 beanPostProcessor
的 postProcessAfterInitialization
在 AbstractAutoProxyCreator
中提供类一个初始化后置处理器 postProcessAfterInitialization
。
1 | public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { |
创建Advice的Bean增强过程如下:
- 每个Bean在执行
invokeInitMethods
进行Init,然后进入到Init后置处理器中 - 通过在
AbstractAutoProxyCreator
的postProcessAfterInitialization
对代理类的包装- 通过
getAdvicesAndAdvisorsForBean
获取所有的可用的advice
增强器。 - 通过
createProxy
创建代理Bean,并返回代理对象。
- 通过
3.Advice获取过程
Advice是进行代理的重要的组成部分,代码中可以通过 @AspectJ
注解在切面类上, 同时使用Advice相关注解如 @After
@Before
等进行标注,声明为一个 Advice
。
实际源码分析时会发现不管是 postProcessBeforeInstantiation
还是 postProcessAfterInstantiation
,最后都会通过 buildAspectJAdvisors
方法寻找所有的候选的 advice。其中有两个比较重要的地方需要注意:
- 为了避免重复,使用了对
aspectBeanNames
的判断,如果判断为空,才会进行Advice
的查找
1 | List<String> aspectNames = this.aspectBeanNames; |
- 通过
getAdvices
获取所有的备选的Advice
1 | List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);// 获取Aspect类中的Advice |
- 通过
getAdvices
中的getAdvisorMethods
获取排除除去Pointcut
标注的所有方法。
1 | private List<Method> getAdvisorMethods(Class<?> aspectClass) { |
- 最后通过
getAdvices
中的getAdvisor
中的getPointcut
方法,通过findAspectJAnnotationOnMethod
对类中不存在Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
进行过滤。
1 | protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) { |
- 最后在
buildAspectJAdvisors
方法中放入到advisorsCache
,作为缓存。
1 | List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);// 获取Aspect类中的Advice |
4.方法的匹配
在进行代理的时候,如何知道对那些方法进行了代理。
- 在后置处理器使用的时候,通过
getAdvicesAndAdvisorsForBean
获取缓存中的Advice
1 | // Create proxy if we have advice. |
- 通过其中的
findEligibleAdvisors
方法,获取可用的Advice - 一步一步点进去后,发现最后使用
canApply
方法进行判断,是否可以对当前Bean
进行代理
1 | public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { |
综上解析中,能够解决一下几个疑问:
AnnotationAwareAspectJAutoProxyCreator 作用是什么
最重要的是引入了
AbstractAutoProxyCreator
,实现对Bean的代理包装;同时在postProcessBeforeInstantiation
时,在使用shouldSkip
时,会获取所有的Advice,并加入到cache中。Spring在什么时候进行AOP代理?
Spring在生成Bean单例时会执行Init方法初始化,通过初始化后postProcessor方法,先获取所有可用的Advice增强器,之后使用ProxyFactory创建Proxy并返回。容器获取Advice的过程?
在前置处理器中,获取@AspectJ中下的所有Advice方法,并添加到缓存中
哪些方法可以被匹配到可以进行AOP?
在PointCut中执行范围中的Bean可以被AOP拦截使用
3. 创建代理的方式
SpringAOP创建代理有两种方式:
- 第一种:使用JDK动态代理
- 第二种:使用CGLIB代理。
下面来针对代理方式的选择方式,查看源码一探究竟。
在上一节中提到的在 AbstractAutoProxyCreator
中的在后置处理器中 postProcessAfterInitialization
的 wrapIfNecessary
中进行了对Bean的代理包装处理.
1 | // 如果存在可用的通知,则对Bean进行代理 |
在 createProxy
中决定了使用哪种方式(JDK动态代理、CGLIB代理)进行代理。
1 |
|
在这里设置了有关于proxyFactory的相关属性, 如是否继承接口的interface,和 ProxyTargetClass 。如果没有实现interface,则会自动将ProxyTargeClass为true,也就是说这两个属性是相对的。
在最后的retrun的 getProxy()
方法最终会调用 DefaultAopProxyFactory
类中的 createAopProxy
方法决定使用哪中代理方式进行代理。
1 | public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { |
前面说的如果自己设置类proxyTargetClass属性为true,就不会判断是继承了interface。如果没有单独设置,则会先判断是否继承了接口,如果没有则会自动设置proxyTargetClass属性为true。
所以得出结论:
- 如果设置了proxyTargetClass为true,则使用CGLIB代理。
- 如果没有设置proxyTargetClass,如果是接口,则使用JDK动态代理。如果没有实现接口,则还是会使用CGLIB进行代理。