向您图文并茂生动讲解Spring AOP 源码(1)

前言

Spring AOP - 注解方式使用介绍(长文详解)中,作者介绍了Spring AOP 注解方式的使用方式。算是给咱们的Spring AOP 源码分析开了个头,做了一点知识点的铺垫。

在开始学习Spring AOP的源码之前,如果你还没有学习过Spring IoC的源码,最好先去学习下Spring IoC。

Spring AOP 只作用于Spring Bean 的特性说明了Spring AOP和Spring IOC 的关系,AOP 依赖于 IOC 容器来管理,后面的源码分析也会涉及到Spring IoC 的源码内容。

下面,假设你已经学习过Spring IoC 的相关内容和Spring AOP的相关使用,让我们开始吧。

本文耗费了作者大量心力,希望能对你有所帮助。

Spring AOP.png

我们前面一直说的Spring AOP源码解析,源码这么多,我们真正关注的内容是什么?

Spring AOP的功能是什么?从使用上直白的说,就是根据我们的配置来生成代理类,拦截指定的方法,将指定的advice织入。

我们应该关注的内容总结下来就是:

  • Spring AOP 的触发时机是什么时候?
  • Spring AOP 是如何解析我们配置的Aspect,生成 Advisors 链的?
  • Spring AOP 是如何生成代理类的,如何将 advice 织入代理类?

另外,整个源码解析的内容过多,为了读者的阅读体验和自己的时间安排。我将按照上面的总结的三点,分三篇向您解读。

本文的源码解析是以AOP注释方式使用来作为例子讲解的,和其他方式主要是在于触发入口不同,核心的流程还是差不多的。希望读者们能够触类旁通。

一、开启AOP自动代理的玄机

我们在Spring AOP - 注解方式使用介绍(长文详解)中介绍了@EnableAspectJAutoProxy 注解,是用来开启 Spring AOP注解的使用。这个的作用就是自动让 ioc 容器中的所有 advisor 来匹配方法,advisor 内部都是有 advice 的,让它们内部的 advice 来执行拦截处理(注:advisor 可以就看成 pointcut + advise的一个组合对象)。引用这个注解的英文翻译就是开启自动代理。

那么里面的玄机是什么呢?

我们进去先进到这个注解里面看看,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;
    boolean exposeProxy() default false;
}

@Import(AspectJAutoProxyRegistrar.class)使用@Import 注解将 AspectJAutoProxyRegistrar注入到 IoC 容器当中。

对这个注解不熟悉的可以去了解一下 @Import Annotation in Spring Framework

我们看一看这个AspectJAutoProxyRegistrar,

AspectJAutoProxyRegistrar.png

注意,这个类实现了ImportBeanDefinitionRegistrar接口。

这个接口是一个Spring 很强大的扩展接口,它的作用是:

Register additional bean definitions when processing @Configuration classes.
Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.

就是说,它需要和@Configuration配合使用,在@Configuration之前已注册的Bean,可以由ImportBeanDefinitionRegistrar接口来处理,这个接口提供了如下一个方法:

void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

这个方法可以拿到@Import的这个class的Annotation Metadata,以及此时的BeanDefinitionRegistry对象,通过BeanDefinitionRegistry 就可以拿到目前所有注册的BeanDefinition,可以自定义逻辑来动态注册一些你觉得必要的BeanDefinition。

PS: 很多开源框架与Spring 集成的时候都扩展了这个接口,比如Apollo的ApolloConfigRegistrar 、mybatis的MapperScannerRegistrar等等

扩展阅读 https://www.logicbig.com/tutorials/spring-framework/spring-core/import-bean-registrar.html

AspectJAutoProxyRegistrar中,实际上就是将AspectJAnnotationAutoProxyCreatorBeanDefinition注册到IoC 容器当中。

下面是AopConfigUtils中执行注册的逻辑代码片段。

image.png

先来一条分割线,理解完上面的流程之后,我们继续来思考。

为什么把AspectJAnnotationAutoProxyCreator注入到Spring IoC 容器中,自动代理就开启了呢?

让我们来寻找这个触发点。

二、自动代理的触发时机

首先,我们来看一下AspectJAnnotationAutoProxyCreator的继承结构。

AnnatationAwareAspectJAutoProxyCreator.png

有没有发现,AspectJAnnotationAutoProxyCreator居然是一个BeanPostProcessor!

学习过 Spring IoC 之后的你,应该对这个类极其的敏感。

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

我们先回顾思考下代理模式的实现思路:(接口) + 真实实现类 + 代理类。

是不是要先有了真实的实现类,才能够生成代理类?!

Spring IoC - 依赖注入 源码解析中我们介绍了Spring Bean 创建的过程,在执行完 Step1 创建实例对象createBeanInstance()和 Step2 属性装配populateBean()之后,我们才算得到一个真正的实现类。

在Step3 initializeBean()中,IoC容器会处理Bean初始化之后的各种回调事件,然后返回一个“可能经过加工”的bean对象。

其中就包括了BeanPostProcessorpostProcessBeforeInitialization 回调 和 postProcessAfterInitialization 回调。

AspectJAnnotationAutoProxyCreator恰恰是一个BeanPostProcessor(原谅我又重复了一次),那就很容易联想到,Spring AOP 就是在这一步,进行代理增强!

三、初探代理类的生成流程

那么接下来,我们就来看看这里面的玄机。

image.png

可以看到实际回调的 postProcessBeforeInitializationpostProcessAfterInitialization 这两个方式是在AbstractAdvisorAutoProxyCreator 中 override 的。

源码位置:AbstractAdvisorAutoProxyCreator

AbstractAdvisorAutoProxyCreator

JavaDoc 很清楚的注明了postProcessAfterInitialization会执行创建代理类的操作,用配置的interceptors 来创建一个代理类,并且告诉我们去看getAdvicesAndAdvisorsForBean,看来这会是一个关键方法,这里我们先不急,继续往下看wrapIfNecessary方法。

源码位置:AbstractAutoProxyCreator#wrapIfNecessary(..)

wrapIfNecessary

这个方法里面有核心的就是两个点,我在上图中分别用**** TODO-1 ******** TODO-2 ****标识出来了。

TODO-1就是获取当前的Spring Bean 适配的 advisors。

TODO-2就是创建代理类


我们接下去的章节就是详细讲解这两个TODO的内容。我们下次再会。

如果本文有帮助到你,希望能点个赞,这是对我的最大动力。

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351