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

30次阅读

共计 3661 个字符,预计需要花费 10 分钟才能阅读完成。

前言

在 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 源码解析,源码这么多,我们真正关注的内容是什么?

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

注意,这个类实现了 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 中执行注册的逻辑代码片段。


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

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

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

二、自动代理的触发时机

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

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

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

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

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

源码位置:AbstractAdvisorAutoProxyCreator

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

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

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

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

TODO-2就是 创建代理类


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

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

正文完
 0