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

2019-11-09 00:00:00 讲解 图文并茂 向您

前言

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

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

  • Spring IoC – Spring IoC 的设计
  • Spring IoC – IoC 容器初始化 源码解析
  • Spring IoC – 依赖注入 源码解析

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

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

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

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

我们前面一直说的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

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

注意,这个类实现了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/tuto…

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

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

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

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

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

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

二、自动代理的触发时机

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

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

有没有发现,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 就是在这一步,进行代理增强

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

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

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

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

源码位置:AbstractAdvisorAutoProxyCreator

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

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

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

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

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

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

TODO-2就是创建代理类

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

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

相关文章