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

Easter79
• 阅读 346

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源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Stella981 Stella981
2年前
Spring Aspect Oriented Programming
  本文是一篇SpringAOP的基础知识分析文章,其中不牵扯源码分析,只包含AOP中重要概念的讲解,分析,以及SpringAOP的用法。    Spring从2.0版本引入了更加简单却强大的基于xml和AspectJ注解的面向切面的编程方式。在深入了解如何用Spring进行面向切面的编程前,我们先了解AOP中的几个重要的基本概念,这几个概念
Easter79 Easter79
2年前
Spring如何实现AOP,请不要再说cglib了!
1\.从注解入手找到对应核心类最近工作中我都是基于注解实现AOP功能,常用的开启AOP的注解是@EnableAspectJAutoProxy,我们就从它入手。!(https://oscimg.oschina.net/oscnet/3307f3a08df1251f4b119de8e02f9a92c10.jpg)
Stella981 Stella981
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Easter79 Easter79
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
2年前
SpringAop的简单实现
AOP当中的概念:1、切入点(Pointcut):在哪些类,哪些方法上切入(where);2、增强(Advice):早期翻译为通知,在方法执行的什么时机(when:方法前/方法后/方法前后)做什么(what:增强的功能);3、切面(Aspect):切面切入点增强,通俗点就是:在什么时机,什么地点,做
Stella981 Stella981
2年前
Invalid property 'driver' of bean class [org.apache.commons.dbcp.BasicDataSource]
Spring整合MyBatis!main方法测试,出现异常:!(https://oscimg.oschina.net/oscnet/up5532e35c99469d9cfcd2bec483942cb367e.png)一堆错,很懵逼!别慌,慢慢分析,也许错误很简单;先分析一下applicationContext.xml文件
Wesley13 Wesley13
2年前
0、Spring 注解驱动开发
0、Spring注解驱动开发0.1简介《Spring注解驱动开发》是一套帮助我们深入了解Spring原理机制的教程;现今SpringBoot、SpringCloud技术非常火热,作为Spring之上的框架,他们大量使用到了Spring的一些底层注解、原理,比如@Conditional、@Import、@
Stella981 Stella981
2年前
Js使用面向对象和面向过程的方法实现拖拽物体的效果
1.面向过程的拖拽实现代码:!(https://oscimg.oschina.net/oscnet/d680c759957babef2fec0902676eaa35ad9.gif)<!DOCTYPEhtml<html<head<titledragDiv</title
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k