向您生动地讲解Spring AOP 源码(1)

Richard_Yi 2019年11月08日 127次浏览

前言

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 - 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的内容。我们下次再会。