Spring AOP原理学习
本文主要介绍Spring中AOP的原理,至于AOP是什么以及如何使用AOP,请参考此处
环境:SpringBoot 2.4.2
有这样的代码
public class MathCalculator {
public int div(int i, int j) {
System.out.println("div..." + i + "/" + j);
return i / j;
}
}
@Aspect
public class LogAspects {
@Pointcut("execution(public int com.hjc.demo.aop.MathCalculator.*(..))")
public void pointCut() {}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("@Before: " + joinPoint.getSignature().getName()
+ "running... args are {" + Arrays.asList(args) + "}");
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("@After: " + joinPoint.getSignature().getName()
+ "div ending... args are {" + Arrays.asList(args) + "}");
}
@AfterReturning(value="pointCut()", returning="result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println("@AfterReturning: " + joinPoint.getSignature().getName()
+ "div return... return value is {" + result + "}");
}
@AfterThrowing(value="pointCut()", throwing="exception")
public void logException(JoinPoint joinPoint, Exception exception) {
System.out.println("@AfterThrowing: " + joinPoint.getSignature().getName()
+ "div has exception... details: {" + exception + "}");
}
}
@EnableAspectJAutoProxy
@Configuration
public class AopConfig {
@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}
进行单元测试,就能显示使用了AOP的结果
@SpringBootTest
class DemoApplicationTests {
@Autowired
private MathCalculator mathCalculator;
@Test
public void testAop() {
mathCalculator.div(1, 1);
}
}
@EnableAspectJAutoProxy
由上面的代码可见,AOP的功能要起作用,@EnableAspectJAutoProxy
注解很关键,不加上这个注解就没有AOP的功能,我们从这个注解出发,研究AOP原理
我们进入这个注解的源码
可以看到@Import({AspectJAutoProxyRegistrar.class})
这一行会给容器中导入AspectJAutoProxyRegistrar
这个类,利用这个类可以自定义给容器中注册bean
在这个类的方法上加上断点,开启debug
进入方法的第一行
可以看到,AspectJAutoProxyRegistrar
这个类给容器中注册了AnnotationAwareAspectJAutoProxyCreator
组件
AnnotationAwareAspectJAutoProxyCreator
接下来,我们主要研究AnnotationAwareAspectJAutoProxyCreator
这个类的作用。查看这个类的源代码,可以看到这个类的继承树
这个类最终是实现了SmartInstantiationAwareBeanPostProcessor
这个后置处理器和BeanFactoryAware
,后置处理器的工作就是在bean初始化前后做一些额外的事情,所以AnnotationAwareAspectJAutoProxyCreator
这个类既是一个后置处理器,又是一个Aware接口的实现类
和后置处理器相关
在IOC容器启动之后,创建和注册了许多BeanPostProcessor
后置处理器,在bean的初始化过程中执行了后置处理器的“before”和“after”方法。在这其中,InstantiationAwareBeanPostProcessor
作为一个后置处理器也创建成功。创建完后,Spring会把BeanPostProcessor
注册到BeanFactory
中,((AbstractBeanFactory)beanFactory).addBeanPostProcessors(postProcessors);
以上便是创建和注册AnnotationAwareAspectJAutoProxyCreator
的大致过程
此处首先区分BeanPostProcessor
和InstantiationAwareBeanPostProcessor
,前者是后者的父类,前者是在Bean对象创建完成初始化前后调用的,而后者是在创建Bean实例之前先尝试用后置处理器返回对象的,这两者的调用时机不同。AnnotationAwareAspectJAutoProxyCreator
属于后者,也就是说AnnotationAwareAspectJAutoProxyCreator
在所有Bean实例创建之前有一个拦截,此时会调用postProcessBeforeInstantiation()
方法,此方法位于其父类AbstractAutoProxyCreator
中,重写了InstantiationAwareBeanPostProcessor
这个接口对应的方法
postProcessBeforeInstantiation
我们开启debug,观察容器中MathCalculator组件创建之前调用postProcessBeforeInstantiation()
方法的时刻
可以看到
-
首先判断当前Bean是否在advisedBeans中,advisedBeans保存了所有需要增强的Bean
-
判断当前Bean是否是基础类型
isInfrastructureClass()
或者是否是切面isAspect()
,所谓基础类型,就是Advice
,Pointcut
,Advisor
,AopInfrastructureBean
如果是,则放入advisedBeans中,这里主要是用来处理切面类 -
判断是否要跳过,不处理这个Bean
postProcessAfterInitialization
在执行完postProcessBeforeInstantiation()
之后,Spring会创建对象
创建完对象后,会执行postProcessAfterInitialization()
方法
这里的关键是这一行代码this.wrapIfNecessary(bean, beanName, cacheKey);
,我们进入这个方法
可以看到
-
首先获取当前Bean匹配的所有增强器,也就是通知方法
具体方法分为三步,首先找到候选的所有增强器,也就是所有通知方法,接着就从中获取能在当前Bean使用的增强器,最后对找到的增强器进行排序
-
保存当前Bean在advisedBeans中
-
如果当前Bean需要增强,就创建Bean的代理对象
获取所有增强器(通知方法),并保存到proxyFactory
中
根据被拦截的Bean是否实现了接口,Spring自动决定使用JdkDynamicAopProxy
(jdk动态代理)还是ObjenesisCglibAopProxy
(cglib动态代理)创建代理对象
-
给容器中返回当前组件用cglib增强了的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程
目标方法执行流程
在创建完代理对象之后,我们来到目标方法的执行处
可以看到,此时的MathCalculator已经是代理对象,这个代理对象里面保存了详细信息,比如增强器,目标对象等
我们进入目标方法,来到CglibAopProxy
的intercept()
方法处,这个方法便是拦截目标方法执行
-
首先根据ProxyFactory对象获取将要执行的目标方法拦截器链,所谓拦截器链,就是每个通知方法被包装为方法拦截器,之后会利用MethodInterceptor机制
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
方法内部首先创建一个List<Object> interceptorList = new ArrayList<>(advisors.length);
遍历所有增强器,将其转为Interceptor
-->>registry.getInterceptors(advisor)
如果增强器已经是MethodInterceptor,直接加入到集合中;如果不是,则使用AdvisorAdapter将增强器转为MethodInterceptor,转换完成返回MethodInterceptor数组 -
如果没有拦截器链,直接执行目标方法
-
如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个
CglibMethodInvocation
,并调用proceed()
方法,并处理得到的返回值
proceed方法中会进行拦截器链的触发过程,拦截器链的触发过程遵循责任链模式
总结
-
使用
@EnableAspectJAutoProxy
注解开启Spring的AOP功能 -
@EnableAspectJAutoProxy
注解会给容器中注册AnnotationAwareAspectJAutoProxyCreator
组件 -
AnnotationAwareAspectJAutoProxyCreator
组件是一个后置处理器 -
在容器的创建流程中会有注册后置处理器的过程,之后便是初始化Bean的过程,在初始化Bean的过程中,该后置处理器会创建业务逻辑组件和切面组件,在组件创建完后,后置处理器会判断该组件是否需要增强,增强的话就将切面的通知方法包装为增强器,并给目标对象创建一个代理对象(使用jdk或者cglib)
-
执行目标方法的过程就是代理对象执行目标方法的过程
使用CglibAopProxy
的intercept()
方法进行拦截的话,会得到目标方法的拦截器链(将增强器包装为MethodInterceptor)
利用拦截器的链式机制,依次进入每一个拦截器进行执行
最终效果:
正常执行:前置通知 -> 目标方法 -> 后置通知 -> 返回通知
出现异常:前置通知 -> 目标方法 -> 后置通知 -> 异常通知