Spring AOP总结——AOP 源码解析(七)

118 阅读4分钟

0.引言

AOP已经分析完成了,整体上自底向上分析,本文对系列文章做些总结,自顶向下简单总结一下。

1. 引入

AOP可以通过@EnableAspectJAutoProxy或者xml的aop:aspectj-autoproxy标签开启,无论哪种方式,都是把对应的BeanPostProcesser引入,加载到Spring的容器中,用于动态代理的创建。

以@EnableAspectJAutoProxy为例,可以按照@EnableAspectJAutoProxy -> @Import(AspectJAutoProxyRegistrar.class) -> implements ImportBeanDefinitionRegistrar这个路径分析,找到AspectJAutoProxyRegistrar,查看AnnotationAwareAspectJAutoProxyCreator是如何注册到spring的容器中的。

一个比较有趣的细节是,这个BeanPostProcessor的优先级是最高的,注册的时候,设置了order是最高优先级,Integer.MIN_VALUE,也就是说,这个BeanPostProcessor中最先执行。

详情见:Spring AOP 引入——AOP 源码解析(六)

2. 创建动态代理

引入了AnnotationAwareAspectJAutoProxyCreator这个BeanPostProcessor,就可以在Bean初始化完成后,对其进行动态代理。核心实现了以下扩展方法

  1. SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference: 解决因循环依赖导致的bean的提前暴露问题。
  2. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation:解决BeanDefinition自定义TargetSource代理问题。
  3. BeanPostProcessor.postProcessAfterInitialization:在Bean初始化完成后,判断是否需要AOP,需要则进行动态代理。

核心逻辑,就在postProcessAfterInitialization中,大致流程为:

  • 找到所有的Advisor,Advisor就是Advice+PointCut,比如我实现了一个@Aspect的类,内部定义了@Before/@AfterReturning的通知方法,在这里就被封装成两个Advisor;
  • 找到合适这个bean的Advisor,也就是遍历上一步获得的Advisor
    • 使用Advisor的PointCut,匹配bean的class下的方法,有一个能匹配,就是符合条件的Advisor
  • 扩展Advisors,这里增加了ExposeInvocationInterceptor,插入在了Advisor链的头部,用于把MethodInvocation放在线程变量内,用于后续的通知使用。
  • 排序,根据一定规则对Advisor进行排序,指定被代理方法执行时,各个Advice的执行顺序;
    1. 复用了AnnotationAwareOrderComparator,也就是我们熟知的Spring的@Order和Order接口的排序规则:
      • 实现PriorityOrdered接口的优先级,高于没有实现该接口的优先级。
      • 获取PriorityOrdered/Ordered/@Order上的顺序,并进行比较,数字越低优先级越高。
      • 没有通过任何方式指定顺序,则优先级最低。
    2. 定义在了一个切面中的通知
      • xml配置的方式,就是定义的顺序
      • 注解配置的方式:
        • 不同类型,按照Around, Before, After, AfterReturning, AfterThrowing方式排序
        • 相同类型,按照名字排序
  • 使用上面的通知链,创建动态代理对象。

详情见:Spring AOP 代理的创建——AOP 源码解析(五)

3.代理实现

3.1 JDK OR CGLIB

默认情况下,如果被代理对象实现了接口,使用JDK代理,否则使用CGLIB,如果开启了optimize或者设置proxyTargetClass=true,则强制使用CGLIB,除非被代理类仅仅是个接口或者本身就是一个JDK代理生成的类。

其实无论哪种实现方式,大致逻辑是相同的,都是在上一步获取的通知链的基础上,使用JDK或者CGLIB技术,在原Bean的基础上,进行封装,对其方法调用进行拦截。拦截后,根据执行的方法,判断链上的哪些通知,需要执行,然后进行链式的调用,有点类似于责任链模式。

其中有些小细节:

  1. 如果exposeProxy=true,则设置当前代理对象到ThreadLocal中,并在finally中还原为旧的代理对象(如果存在)。
  2. 如果返回的是被代理对象本身,需要替换为代理对象,否则就会把未代理对象暴露出去。

详情见: Spring AOP 代理的实现——AOP 源码解析(四)

3.2 通知链调用

无论是哪种实现方式,最终都落到了通知链的调用,实现的源码在ReflectiveMethodInvocation中。简单说,就是对可以应用在该方法上的通知,进行链式调用,有点类似于责任链。一个小细节是,有些通知还需要根据参数进一步判断,是否能应用在该方法上(切点上有参数相关约束)。

详情见:Spring AOP 通知的调用——AOP 源码解析(三)

4.通知的实现

Spring AOP把各种通知,抽象成了一个接口MethodInterceptor,无论哪种通知都需要实现它。其实实现的代码也非常简单,前置通知,就先执行通知,再执行原方法。后置通知就是,限制性原方法,在finally中执行后置通知。各种通知的实现类为:

  • @Before - MethodBeforeAdviceInterceptor
  • @After - AspectJAfterAdvice
  • @AfterReturning - AfterReturningAdviceInterceptor
  • @AfterThrowing - AspectJAfterThrowingAdvice
  • @Around - AspectJAroundAdvice

详情见:Spring AOP 通知的实现——AOP 源码解析(二)

系列所有文章

  1. Spring AOP 概念 & 应用 ——AOP 源码解析(一)
  2. Spring AOP 通知的实现——AOP 源码解析(二)
  3. Spring AOP 通知的调用——AOP 源码解析(三)
  4. Spring AOP 代理的实现——AOP 源码解析(四)
  5. Spring AOP 代理的创建——AOP 源码解析(五)
  6. Spring AOP 引入——AOP 源码解析(六)