Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

Easter79
• 阅读 259

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

本文包含以下内容:

  1. postProcessBeforeInstantiation 执行时机

  2. postProcessBeforeInstantiation  的具体作用

  3. 目标方法div执行流程一:获取拦截器链

  4. 目标方法div执行流程二:拦截器链的调用流程分析

  5. AOP 总结

前文回顾:

Spring注解驱动开发之十三——AOP原理分析(一)@EnableAspectJAutoProxy注解干了什么

上文说到,通过 @EnableAspectJAutoProxy 注解,将通过注册器往Spring中注册一个 AnnotationAwareAspectJAutoProxyCreator 实例化对象,并且讨论了AnnotationAwareAspectJAutoProxyCreator  的继承关系。

本文将紧接上文,进行断点调试在 registerBeanPostProcessors 函数之后讨论, AnnotationAwareAspectJAutoProxyCreator   实例化组件如何实现AOP 的功能。

1.postProcessBeforeInstantiation 执行时机

在postProcessBeforeInstantiation 函数添加断点,查看执行时机

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

调用栈如图所示:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

1):84, AnnotationConfigApplicationContext

refresh:543, AbstractApplicationContext

调用栈跟上文类似,上文,注册了BeanPostProcessors 现在运行 finishBeanFactoryInitialization 函数

根据注释:注册所有剩余得“非懒加载”得单例

// Instantiate all remaining (non-lazy-init) singletons.

2)进入到内部,是创建Bean 得流程

finishBeanFactoryInitialization:867, AbstractApplicationContext

preInstantiateSingletons:761, DefaultListableBeanFactory

getBean:197, AbstractBeanFactory

doGetBean:302, AbstractBeanFactory

getBean->doGetBean()->getSingleton()

主流程

可以看到 sharedInstance 之前,尝试获取,确认容器内没有才进行创建

// Eagerly check singleton cache for manually registered singletons.

3)getSingleton:230, DefaultSingletonBeanRegistry

getObject:306, AbstractBeanFactory$1

createBean:473, AbstractAutowireCapableBeanFactory

在这里可以看到注释 :让BeanPostProcessors有机会 返回一个代理 而不是原有bean实例。

所以在获取到的Bean  ==null 时在后面将会进行 doCreateBean(beanName, mbdToUse, args); 创建Bean

try {

查看函数可以看到,跟上文的 创建Bean 的一样

实例化Bean

if (instanceWrapper == null) {

赋值以及初始化

populateBean(beanName, mbd, instanceWrapper);

4) 进入 resolveBeforeInstantiation 函数

resolveBeforeInstantiation:1011, AbstractAutowireCapableBeanFactory

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {

看到函数 中:

 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

进入到函数 applyBeanPostProcessorsBeforeInstantiation

查看是否实现InstantiationAwareBeanPostProcessor ,如果是则执行postProcessBeforeInstantiation ,如果返回不为null 则运行applyBeanPostProcessorsAfterInitialization 。所以在这找到了Before 和After

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {

从而找到了, postProcessBeforeInstantiation 、applyBeanPostProcessorsAfterInitialization 执行时机 并且得出结论:

AnnotationAwareAspectJAutoProxyCreator  在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()

2.postProcessBeforeInstantiation 具体作用

我们分别对自定义得MathCalculator进行断点调试, postProcessBeforeInstantiation 函数代码如下所示。

@Override

1. MathCalculator ,放行跳转到 MathCalculator 

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

1.判断是否包含在增强adviseBeans 中

    if (this.advisedBeans.containsKey(cacheKey)) {

2.判断 2个函数判断

if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {

2.1 isInfrastructureClass 判断是不是 Advice、 Pointcut、Adviso r、AopInfras tructureBean 类型

protected boolean isInfrastructureClass(Class<?> beanClass) {

以及 判断是否是切面(@Aspect)

@Override

isAs pect 函数,判断具体是否巨有Aspect.class 这个注解

@Override

2.2  shouldSkip 判断是否应该跳过,获取到所有 Advisor 判断是否实现

AspectJPointcutAdvisor 判断是否跳过

@Override

 可以断点看到当前,并不是 AspectJPointcutAdvisor 所以直接返回null

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

接下来会调用 new方法创建实例 

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

3.然后创建完成后调用postProcessAfterInitialization函数 

/**

看到 wrapIfNecessary 函数,如下

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {

3.1在wrapIfNecessary函数 中,判断targetSourcedBeans 列表里面是否,创建过当前Bean

if (beanName != null && this.targetSourcedBeans.contains(beanName)) {

3.2 判断修饰过的advisedBeans是否包含

if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {

3.3与上面2.1 和2.2相同 判断是否代理,是否跳过

if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {

3.4接下来判断过后调用getAdvicesAndAdvisorsForBean函数中的findEligibleAdvisors 找到所有的能用的通知方法并进行排序

@Override

3.5 将当前Bean 放入advisedBeans 中表示已经被增强处理过并且通过createProxy 获得代理对象 并返回

if (specificInterceptors != DO_NOT_PROXY) {

通过代理工厂创建代码如下

protected Object createProxy(

可以看到有CglibAopProxy 和  ObjenesisCglibAopProxy ,然后在容器获取对象时,将会进行返回代理对象。这就是postProcessBeforeInstantiation 具体作用

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

3.目标方法div执行流程一:获取拦截器链

对容器获取到的对象,添加断点,查看div 目标方法的执行流程

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

可以看到如下图,所示,mathCalculator 对象是经过Cglib 代理后的对象

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

1.进入方法可以看到进入CglibAopProxy.intercept() 方法进行代理拦截,代码如下

@Override

2.其核心是通过getInterceptorsAndDynamicInterceptionAdvice函数,获取拦截器列表,判断列表是否为空来进行不同的操作

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

3.进入获取拦截器列表的函数

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {

4.看到getInterceptorsAndDynamicInterceptionAdvice函数,registry.getInterceptors(advisor);遍历所有的增强器,返回interceptorList

@Override

5.核心在于registry.getInterceptors(advisor); 将增强器转为List;如果是MethodInterceptor,直接加入到集合中,如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;转换完成返回MethodInterceptor数组;

@Override

4.目标方法div执行流程二:拦截器链的调用流程分析

运行完成后,将获得拦截器如下所示,有5个:1个默认的4个自定义的增强方法

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

拦截器不为空,将会运行CglibMethodInvocation.proceed()方法

else {

可以看到具体的process()函数代码如下

@Override

1.如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;

if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {

2.接着获取第0个Advice :org.springframework.aop.interceptor.ExposeInvocationInterceptor执行

else {

3.查看invoke()的代码如下,重复调用到这个mi.proceed() 使得取下一个拦截器,进行操作

流程如图所示:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

4.再次调用proceed(),运行到AspectJAfterThrowingAdvice第二个拦截器:同样是调用invoke(this)

@Override

流程如图所示:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

5.再次调用proceed(),运行到AfterReturningAdviceInterceptor第三个拦截器:同样是调用invoke(this)

@Override

流程图如下:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

6.再次调用proceed(),运行到AspectJAfterAdvice第四个拦截器:同样是调用invoke(this),但是此处进行了finally ,无论如何都会运行advice的函数。

@Override

流程图如下:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

7.再次调用proceed(),运行到MethodBeforeAdviceInterceptor第五个拦截器:同样是调用invoke(this),但是此处进行了finally ,无论如何都会运行advice的before函数调用前置通知、以及目标函数。

@Override

流程图如下:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

8.再次运行proceed()函数检测到是最后一个拦截器,并且进行出栈操作。往上返回到上一级,由于第6步的proceed()方法返回就会触发finnaly 的函数进行 调用后置通知,流程图如下:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

9.在第5步,可以看到代码,没有问题才会调用afterReturn 函数完成的回调,所以第6步发生异常直接跳到第4步的,捕获异常并且调用异常的回调方法。流程图如下:

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

5.AOP 总结

1)、  @EnableAspectJAutoProxy 开启AOP功能

2)、@EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator

3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;

4)、容器的创建流程:

1)、 registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象

2)、 finishBeanFactoryInitialization()初始化剩下的单实例bean

        1)、创建业务逻辑组件和切面组件

2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程

3)、组件创建完之后, 判断组件是否需要增强

是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);

5)、执行目标方法:

1)、代理对象执行目标方法

2)、CglibAopProxy.intercept();

1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)

2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;

3)、效果:

              正常执行:前置通知-》目标方法-》后置通知-》返回通知

              出现异常:前置通知-》目标方法-》后置通知-》异常通知

-END-

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

可以关注我的公众号,免费获取价值1980元****学习资料

点击“在看”,学多少都不会忘~

Spring注解驱动开发之十四——AOP原理分析(二)Annotation...AutoProxyCreator 实现时机

本文分享自微信公众号 - 阿聪的全栈之路(gh_ffab7c84fb0c)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
光头强的博客 光头强的博客
6个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
6个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
小森森 小森森
3天前
租房类微信小程序-基于微信云开发-小程序端集成了管理员后台-一键部署,快速发布
温馨提醒本项目使用MITLicense协议,仅适用于学习交流,并且不提供无偿的、不提供无偿的、不提供无偿的维护修改服务(但可提issue)。若直接将本项目用于商用,因本项目带来的所有后果由使用者自行承担。如需商用升级版,请联系我微信,微信二维码在本博客页面右上角在此奉劝某些人,请尊重作者的劳动成果,做人积点德吧!最近发现有人拿我的源码进行二次分
小森森 小森森
3天前
计划助手V1.0-微信小程序(QQ小程序)-源代码分享
疫情期间在家感觉好无聊啊,于是利用空闲时间做了一个用来记录和管理小目标时间的小程序,命名为《小沙漏》。QQ版本小程序同步上线,QQ小程序叫《时间小沙漏》,欢迎大家前来体验,后期也会添加其他的新功能哦【区别】:微信小程序的代码与QQ小程序的源码是不一样的。微信小程序的源码基于微信小程序云开发,需要在有网络的情况下使用,具有同步功能,所有记录在删除小
小森森 小森森
6个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
晴空闲云 晴空闲云
6个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
5个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Wesley13 Wesley13
1年前
0、Spring 注解驱动开发
0、Spring注解驱动开发0.1简介《Spring注解驱动开发》是一套帮助我们深入了解Spring原理机制的教程;现今SpringBoot、SpringCloud技术非常火热,作为Spring之上的框架,他们大量使用到了Spring的一些底层注解、原理,比如@Conditional、@Import、@
密钥管理系统-为你的天翼云资产上把“锁
本文关键词:数据安全,密码机,密钥管理一、你的云上资产真的安全么?1.2021年1月,巴西的一个数据库30TB数据被破坏,泄露的数据包含有1.04亿辆汽车和约4000万家公司的详细信息,受影响的人员数量可能有2.2亿;2.2021年2月,广受欢迎的音频聊天室应用Clubhouse的用户数据被恶意黑客或间谍窃取。据悉,一位身份不明的用户能够将Clubho
NVIDIA安培架构下MIG技术分析
关键词:NVIDIA、MIG、安培一什么是MIG2020年5月,NVIDIA发布了最新的GPU架构:安培,以及基于安培架构的最新的GPU:A100。安培提供了许多新的特性,MIG是其中一项非常重要的新特性。MIG的全名是MultiInstanceGPU。NVIDIA安培架构中的MIG模式可以在A100GPU上并行运行七个作业。多实
helloworld_28799839 helloworld_28799839
6个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue