spring注解驱动开发-6 Spring AOP实现原理

前言

前面我们对AOP进行了简单实现,那它是如何帮助我们实现相关通知方法的?它的底层是如何做到的?今天我们来探讨下。源码的学习一直是难啃的硬骨头,但是啃下了,对编程和面试都有很大的帮助。本篇文章只是个人的简单理解,想要真正深入的理解,建议:
阅读相关书籍,打断点进行源码调试。文章课程链接:尚硅谷spring注解驱动教程(雷神)
我们可以通过以下方式学习原理

  • 往容器中注册了什么组件
  • 组件在什么时候工作
  • 组件能够帮我们做些什么

1.@EnableAspectJAutoProxy注解原理

我们实现AOP,只需在注解类加上@EnableAspectJAutoProxy就可生效,因此这个注解是我们研究源码的入口,首先看看这个注解,如图:

在这里插入图片描述

我们注意到,该注解又用@Import导入了一个组件(AspectJAutoProxyRegistrar),我们再点进去看看,如图:

在这里插入图片描述

AspectJAutoProxyRegistrar类实现了ImportBeanDefinitionRegistrar,我们在前面讲到,实现ImportBeanDefinitionRegistrar可以帮我们自定义的往容器中注册组件。那下面我们就打一个断点,看看这里往容器中注册了什么组件。进入registerAspectJAnnotationAutoProxyCreatorIfNecessary() 方法中,点进来如下:

在这里插入图片描述

可以看到该方法又掉用了下面这个方法,继续跟进,这里的 registerOrEscalateApcAsRequired() 方法第一个参数是传了AnnotationAwareAspectJAutoProxyCreator.class,点进来如下:

在这里插入图片描述

代码首先判断registry中是否已经包含了org.springframework.aop.config.internalAutoProxyCreator,我们第一次进入,当然是不存在,因此走下面的 else 逻辑

  • 首先是创建一个bean定义信息:RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); 注意传入的cls是 AnnotationAwareAspectJAutoProxyCreator 的class对象
  • 中间的代码设置了各种属性
  • 最后往 registry 中注册了一个bean定义信息,名称为 org.springframework.aop.config.internalAutoProxyCreator ,bean定义为创建AnnotationAwareAspectJAutoProxyCreator

整个过程最终就是往容器中注册一个AnnotationAwareAspectJAutoProxyCreator 组件(自动代理创建器),组件id为 org.springframework.aop.config.internalAutoProxyCreator

我们回到 AspectJAutoProxyRegistrar ,看下一句代码,如图:

在这里插入图片描述

这里是把 EnableAspectJAutoProxy 注解的内容信息拿到,并在后面做一些判断和操作。

前面的分析中,我们知道,@EnableAspectJAutoProxy 注解其实就是帮我们注册了一个 AnnotationAwareAspectJAutoProxyCreator 组件,下面我们继续研究。

2.AnnotationAwareAspectJAutoProxyCreator 分析

1.分析前工作,给相关方法打上断点

首先,我们还是先进入到源码,可以看到,继承了AspectJAwareAdvisorAutoProxyCreator。

在这里插入图片描述

这里,我们先理清该组件的继承关系。这里代码截图不一一展示,通过源码得出以下继承关系(idea工具快捷键 Ctrl+Alt+Shift+U):
在这里插入图片描述

继承关系

梳理继承关系后,我们来分析研究下 AbstractAutoProxyCreator ,上图红框上方第二个,此类又继承了 ProxyProcessorSupport 并实现了 SmartInstantiationAwareBeanPostProcessor(bean的后置处理器)和BeanFactoryAware,下一步我们点到 BeanFactoryAware 中,如图:

在这里插入图片描述

此接口能将bean工厂给传进来,下面我们看下SmartInstantiationAwareBeanPostProcessor 这个后置处理器,所谓后置处理器,就是在bean初始化完成前后做事情。源码如图:

在这里插入图片描述

下一步,我们在 AbstractAutoProxyCreator 上把实现的方法打上相应的断点。其中有 BeanFactoryAware 的 setBeanFactory() 方法和所有与后置处理器有关的方法,如图(部分):

在这里插入图片描述
在这里插入图片描述

打上之后,我们回到此类(AbstractAutoProxyCreator)的子类(AbstractAdvisorAutoProxyCreator)看一下源码,发现其重写了setBeanFactory()方法,并调用了initBeanFactory(),如图:

在这里插入图片描述

我们在看看 AbstractAdvisorAutoProxyCreator 中是否有后置处理器相关的方法,有的话打上断点(此类没有)。然后在回到 AbstractAdvisorAutoProxyCreator 的子类(AspectJAwareAdvisorAutoProxyCreator)是否有相关的方法(后置处理器方法或setBeanFactory()),有的话继续打断点,就这样层层找回去,直到 AnnotationAwareAspectJAutoProxyCreator,点进源码,我们发现有一个 initBeanFactory() 方法,似乎与前面的initBeanFactory() 方法有联系,先打上断点,如图:

在这里插入图片描述

最后,我们在自己的配置文件中加上注解(断点在组件注册的地方),此配置文件在文章《AOP实现中》创建,如图:

在这里插入图片描述

2.源码调试分析

1.创建过程

启动我们的测试类,首先来到 AbstractAdvisorAutoProxyCreator 的 setBeanFactory() 方法,如图:

在这里插入图片描述

那他是如何到达这一步的呐,我们从自己写的测试方法开始(在文章《AOP实现》中编写),先看看方法栈,如图:

在这里插入图片描述
分析过程请看视频,图片分析排版乱且不易编写,这里记录分析结果:

  1. 传入配置类,创建IOC容器,相应代码 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
  2. 注册配置类,刷新容器,调用方法 refresh();
  3. registerBeanPostProcessors(beanFactory); 注册bean的后置处理器来实现拦截bean的创建
    1)先获取IOC容器中已经定义了的需要创建对象的所有BeanPostProcessor
    2)给容器中添加其他 BeanPostProcessor
    3)优先注册实现了priorityOrdered接口的 BeanPostProcessor
    4)再注册实现了 Ordered 接口的 BeanPostProcessor
    5)最后注册没有实现优先级接口的 BeanPostProcessor
    6)注册 BeanPostProcessor 实际上就是创建 BeanPostProcessor 对象,保存在容器中
    创建 internalAutoProxyCreator 的 BeanPostProcessor 【AnnotationAwareAspectJAutoProxyCreator】过程
    –6.1)创建bean实例
    –6.2)populateBean,给bean的各种属性赋值
    –6.3)initializeBean:初始化bean
    ----6.3.1)invokeAwareMethods(),处理Aware接口的方法回调,这一步就调到了 setBeanFactory(),如图:
    在这里插入图片描述
    ----6.3.2)applyBeanPostProcessorsBeforeInitialization(),应用后置处理器的 PostProcessorsBeforeInitialization()
    ----6.3.3)invokeInitMethods():执行自定义的初始化方法
    ----6.3.4)applyBeanPostProcessorsAfterInitialization(),执行后置处理器的 PostProcessorsAfterInitialization()
    –6.4)BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator) 创建成功,并
    7)把 BeanPostProcessor 注册到 BeanFactory 中( beanFactory.addBeanPostProcessor(postProcessor) )
    以上为 AnnotationAwareAspectJAutoProxyCreator 的创建过程,且是InstantiationAwareBeanPostProcessor 类型的后置处理器,可以通过继承图看到
  4. finishBeanFactoryInitialization(beanFactory);完成BeanFactory的初始化工作,创建剩下的单实例bean
    1)遍历获取容器中的所有bean,依次创建对象:getBean(beanName);方法依次调用
    getBean -> doGetBean() -> getSingleton()
    2)创建bean【AnnotationAwareAspectJAutoProxyCreator :所有bean创建之前会有一个拦截】
    –2.1)先从缓存中获取当前bean,如果能获取到(bean是在之前创建过的),直接使用,否则再创建;只要创建好的Bean都会被缓存起来
    –2.2)createBean(); 创建bean
    【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
    【InstantiationAwareBeanPostProcessor 是在创建Bean实例之前先尝试用后置处理器返回对象】
    因此,AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean实例
    ----2.2.1)resolveBeforeInstantiation(beanName,mbdToUse); 解析BeforeInstantiation,希望后置处理器在此能返回一个代理对象,如果能返回代理对象就使用,不能就继续
    ----2.2.2)doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例,和前面的流程是一样的

2.作用(结合源码打断点调试理解)

  1. 每一个bean创建之前,调用 postProcessBeforeInstantiation(),
    以自己写的aop测试类为例
    1)判断当前bean是否在advisedBeans中(保存了所有需要增强的bean)
    2)判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
    3)是否需要跳过
    –3.1)获取候选的增强器(切面中的通知方法)List candidateAdvisors,每一个封装的通知方法的增强器类型为 InstantiationModelAwarePointcutAdvisor,
    判断每一个增强器是否是AspectJpointcutAdvisor类型的,返回true
    –3.2)其他返回false
  2. 创建对象
    postProcessAfterInstantiation
    return wrapIfNecessary(bean, beanName, cacheKey);//如果需要的情况下,进行包装
    1)获取当前bean的所有增强器(通知方法)Object[] specificInterceptors
    1.1)找到候选的所有增强器(找哪些通知方法是需要切入当前bean方法的)
    1.2)获取到能在bean中使用的增强器
    1.3)给增强器排序
    2)保存当前bean 在 advisedBeans 中
    3)如果当前bean需要增强,创建当前bean的代理对象
    3.1)获取所有增强器(通知方法)
    3.2)保存到 proxyFactory 中
    3.3)创建代理对象,spring自动决定,分别为 JKD动态代理和 cglib动态代理
    4)给容器中返回当前组件使用cglib增强了的代理对象
    5)以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程。
  3. 目标方法的执行
    容器中保存了组建的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象等)
    1)CglibAopProxy.intercept();拦截目标方法的执行
    2)根据ProxyFactory对象获取将要执行的目标方法拦截器链
    List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    –3.1)List interceptorList保存所有拦截器链(一个默认的ExposeInvocationInterceptor和自己定义的增强器)
    –3.2)遍历所有的增强器,将其转为Interceptor
    –3.3)将增强器转为 List ,如果是 MethodInterceptor ,直接放到容器中,如果不是,使用AdvisorAdapter 将增强器转为 MethodInterceptor,转换后返回
    3)如果没有拦截器链,直接执行目标方法
    拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
    4)如果有拦截器链,把需要执行的目标对象、目标方法、拦截器链等信息传入创建一个 CglibMethodInvocation 对象,并调用其proceed() 方法(触发过程)
    5)拦截器链的触发过程(详细分析)

首先,我们将断点打到目标方法的执行上,如图,debug运行

在这里插入图片描述

运行后,来到断点处,直接进入方法(step into),来到这个方法,第一个红框为获取拦截器链,上面对其进行了分析,在下面的红框处打上断点,准备分析拦截器链的执行流程

在这里插入图片描述

首先看看当前的拦截器链,如图

在这里插入图片描述

我们进入到 xx.proceed() 方法,如图,我们来到了 CglibAopProxy的 proceed()方法,我们再点击进入

在这里插入图片描述

我们来到了 ReflectiveMethodInvocation 的 proceed() 方法,这里首先是一个判断,如果没有拦截器执行目标方法(size = 0),或者拦截器的索引(this.currentInterceptorIndex)和拦截器数组-1大小一样(执行到最后一个拦截器)执行目标方法(return this.invokeJoinpoint();)。此时,currentInterceptorIndex = -1,下一步 currentInterceptorIndex 会先自增,用于获取当前拦截器,我们给下面的 return ((Mxxx).invoke(this) 打上断点。我们进入这个 invoke(this) 方法。

在这里插入图片描述

我们来到了 ExposeInvocationInterceptor 的 invoke() 方法,如图,我们进入到红框的方法中(这里我点击下一步直接就跳过了,只有点击放行,就又到了ReflectiveMethodInvocation 的 proceed() 方法)

在这里插入图片描述

进入方法后,我们发现再次来到了 ReflectiveMethodInvocation 的 proceed() 方法,如图,此时 索引 = 1,拦截器为:前置通知,再次进入到 invoke() 方法。

在这里插入图片描述

我们进入到了 MethodBeforeAdviceInterceptor 的 invoke() 方法,它首先执行了前置通知方法,然后执行 mi.proceed() 方法,我们进入这个方法。

在这里插入图片描述
我们发现,我们再次来到了 CglibAopProxy的proceed() ,和前面的流程一样,我们点进去,自然而然我们再次来到了 ReflectiveMethodInvocation 的 proceed() 方法,如图,此时 索引 = 2,拦截器为:后置(返回)后置,再次进入到 invoke() 方法。

在这里插入图片描述

来到 AspectJAfterAdvice 的 invoke() 方法,这里注意,首先是执行 mi.proceed() ,等返回后再去执行后置通知方法(最后一个执行),且方法在 finally 语句块中,这也解释了为什么目标方法是否正常返回都会执行的原因,我们再次进入到 proceed() 方法

在这里插入图片描述

下面的步骤是一样的,不再赘述,这里只谈谈每个通知类型的 invoke() 方法:
再次进入到 ReflectiveMethodInvocation 的 proceed() 方法 ,索引 = 3,拦截器:正常返回通知,进入到 invoke() 方法

在这里插入图片描述

正常返回通知方法在 mi.proceed()下(下一个掉异常返回通知,然后返回),如果有异常,就直接抛给上一个方法了,因此,正常返回通知在发生异常时不会执行,我们再次进入 mi.proceed()
此时 索引 = 4,拦截器:异常返回通知,进入方法,如图

在这里插入图片描述

可以看到,异常返回通知会捕获异常,并抛出异常,再次进入 ReflectiveMethodInvocation 的 proceed() 方法,来到第一个判断,索引为 4 等于 拦截器链(size = 5)- 1,执行目标方法,如图

在这里插入图片描述
执行目标方法后,返回到异常返回通知,如果有异常就捕获执行,没有返回到正常返回通知,返回后置通知,至此,整个拦截器链的执行流程就分析完了。

总结

  1. @EnableAspectJAutoProxy 开启AOP功能
  2. @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
  3. AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器 (InstantiationAwareBeanPostProcessor类型)
  4. 容器的创建流程:
    1)this.registerBeanPostProcessors(beanFactory); 注册后置处理器,创建 AnnotationAwareAspectJAutoProxyCreator 对象
    2)this.finishBeanFactoryInitialization(beanFactory);初始化剩下的单实例bean
    2.1)创建业务逻辑组件和切面组件
    2.2)AnnotationAwareAspectJAutoProxyCreator 拦截组件的创建过程
    2.3)组件创建完成之后,判断组件是否需要增强,是:切面的通知方法,包装成增强器(Advisor)给业务逻辑组件创建一个代理对象(cglib)
  5. 执行目标方法:
    1)代理对象执行目标方法
    2)CglibAopProxy.intercept();
    2.1)得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
    2.2)利用拦截器的链式机制,依次进入到每一个拦截器进行执行
    2.3)效果(spring版本有区别):正常流程:前置通知 -> 目标方法 -> 正常返回通知 -> 后置通知
    异常流程:前置通知 -> 目标方法 -> 异常返回通知 -> 后置通知

end…

如果总结的还行,就点个赞呗 @_@ 如有错误,欢迎指点,下一篇spring注解驱动开发-7 Spring声明式事务···

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值