转载

简述 Spring AOP 的设计理念和源码实现

不论是采用 XML 还是注解方式,最终 Spring 读取加载后都会生成与之对应的 BeanDefinition,然后利用它就可以去实例化一个对象。

BeanDefinition 用来描述创建一个实例所需要的信息

简单的看来创建一个对象其实就 2 步骤

  1. 读取注解类,然后根据注解或者 XML 配置文件,将其封装成 BeanDefinition
  2. 根据 BeanDefinition 包含的信息来实例化一个对象

Spring 就在这几个步骤中穿插逻辑,从而拓展出了一个生态,下面我们就来看看 AOP 的实现原理

示例代码

简述 Spring AOP 的设计理念和源码实现
简述 Spring AOP 的设计理念和源码实现
简述 Spring AOP 的设计理念和源码实现
简述 Spring AOP 的设计理念和源码实现

基本用法这里就不再赘述

装载 BeanDefinition

调用 BeanFactoryProcessor

new AnnotationConfigApplicationContext(Class<?>... componentClasses) 首先将 AppConfig 解析为 BeanDefinition 放入 BeanFactoryBeanDefinitionMap

然后调用 refresh() 方法中,会调用 invokeBeanFactoryPostProcessors(beanFactory); 在这个方法中去调用所有的 BeanFactoryProcessor

那么什么是 BeanFactoryProcessor 它又有什么作用呢?看下面这段代码

简述 Spring AOP 的设计理念和源码实现

通过实现了 BeanFactoryProcessor 我们可以拿到 ConfigurableListableBeanFactory ,通过容器我们就能拿到 BeanDefinition 进行分析和修改,从而间接的影响 Bean 的实例化做到我们无感知,无侵入的增强和拓展类的功能

简述 Spring AOP 的设计理念和源码实现

调用 BeanDefinitionRegistryPostProcessor

然后该方法中,会继续调用 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); (还调用了其它的方法,本文只讨论这个方法), BeanDefinitionRegistryPostProcessor 它的作用是可以像容器中添加新的 BeanDefinition ,在这一步将解析 JavaConfig 对应的 BeanDefinition ,然后解析 @ComponentScan(basePackages = "com.example.ana") 这个注解,将这个包下面的所有类,装载为 BeanDefinition 然后放入 BeanFactory 容器中

到此为止,包下面的所有类就已经扫描装载完毕在 BeanFactoryBeanDefinitionMap

实例化

找到所有的 @Aspect

要想应用 advice 逻辑,首先我们需要找到对应的切面,然后我们在其中查看需要在哪些方法中织入对应的 advice 逻辑,比如在 UserServiceImplqueryUser 这个 JoinPoint 切点之后织入 afterQuery

这分为 2 步,先来看是如何查找切面 @Aspect 的,从 refresh() 中看起走,到这个位置

简述 Spring AOP 的设计理念和源码实现

在 createBean 中调用了 resolveBeforeInstantiation 这个方法,目的就是给 BeanPostProcessor 一个返回代理对象的机会,比如下图将所有 beanName 名字为 a 的对象替换成对象 C

简述 Spring AOP 的设计理念和源码实现

它有它的作用比如

  1. 制特定目标bean的默认实例化
  2. 执行值的注入等

我们这里是为了寻找对应的 @Aspect 接着往下看,获取所有的 BeanPostProcessor 需要注意的是除去我们自己定义的 BeanPostProcessor 之外还有框架自身的一些实现类,然后如果发现类是 InstantiationAwareBeanPostProcessor 的话

  1. 取出 BeanFactory 中的所有的 bean 的类型
  2. 通过反射判断对应的类型是否实现了 @Aspect 这个注解
  3. 然后将注解类对应的 advice 的相关信息缓存到 advisorsCache

首次调用 BeanPostProcessor 的实现就会做以上这件事,后续的调用就直接从缓存中取用即可

检测对象是否需要被代理

现在所有的 @Aspect 都已经缓存好了,现在就要知道如何在调动业务逻辑的时候,如何织入对应的 advice 逻辑,比如在调用一个事务方法的时候,系统是怎么帮我做到,自动打开事务,方法结束的时候提交事务的,这就需要一个代理对象帮我去做这件事情,那么什么时候该创建代理对象,代理对象又是如何执行对应的 advice 方法呢

举个很简单的例子

User user = new User("小李");
user.getName();
复制代码

这里就只有一个获取名称的方法,其它什么都不需要,那你说还需要为它创建代理对象吗,肯定不需要,再看下面这个

@Transactional
public void saveXXX() {
    xxxDao.saveUser();
    xxxDao.saveProject();
}
复制代码

这个方法我们添加了事务,要求只要其中一个失败就得进行回滚从而保证这个操作是一个原子操作,这个方法肯定就需要创建代理对象了

就这个例子来看,当我们发现所调用方法的注解具有一定的逻辑功能的话就需要创建代理对象,拓展来看就是说,我们需要判断一个类,或者类中是否存在某个方法需要被增强处理,从而来决定是否创建代理对象

那么在我们这个例子中,在实例化每个对象的时候,就是首先从 advisorsCache 取出 advisor 检测当前类是否满足 pointcutexpression 条件,满足的话就需要为其创建代理对象,并且将匹配到的 advisor 存入代理对象中,最后容器中最后的实现类也变成了对应的代理类

简述 Spring AOP 的设计理念和源码实现

创建代理对象

1) 早期 bean 对象的创建

resolveBeforeInstantiation 方法后就会调用 doCreateBean 来创建实例,在这里面首先会创建一个早期 bean 对象,这个对象主要是用于解决循环依赖问题,比如

class A {
    B b;
}
class B {
    A a;
}
复制代码

他们都是单例,Spring 在实例化 A 的时候,发现依赖了 B 需要去实例化 B,在实例化 B 的时候又发现需要实例化 A,造成循环依赖无法实例化成功

Spring 解决这个问题的核心思想非常简单,就是先创建一个早期 A 对象,仅仅是一个没有填充属性的引用,然后去创建 B 对象的时候再填充 A 就成功了,方法递归返回 A 对象也就创建成功了,容器已经持有了对应的引用,那么在后续填充完各自的属性后就算创建完整了

2)填充 Bean 的相关属性

上一步操作得到了一个早期的 bean 对象,并且放入了早期容器中下一步就是调用 populateBean 对 bean 的属性就行填充,主要做以下 2 件事情

  • 注入依赖对象(处理 @Autowired )
    • 如果依赖的对象未被创建,则会进行递归创建
  • 处理 @Value 将配置文件中的值注入到实例中去

3)初始化 Bean

如果类实现了 InitializingBean 接口,就调用它的 afterPropertiesSet 进行值的设置

如果 XML 定义了 init-method 的话后续就会调用 invokeCustomInitMethod ,然后调用对应的初始化方法进行初始化

4)创建代理实例

在初始化 Bean 完成后就会调用 applyBeanPostProcessorsAfterInitialization ,旨在初始化完成后再给与一次修改 Bean 的机会

简述 Spring AOP 的设计理念和源码实现

在这个方法中会去获取所有的 BeanPostProcessor 然后依次调用其 postProcessAfterInitialization 方法

除去我们自己实现了 BeanPostProcessor 之外,框架本身还提供了很多用于自身使用,这些 Processor 会在 refresh() 中的 registerBeanPostProcessors 方法中进行注册和排序

其中有一个是 AbstractAutoProxyCreator 的实现,调用它的 postProcessAfterInitialization 里面会调用 wrapIfNecessary ,这个方法首先它会去找到所有候选的 advisor ,然后判断当前类或者实例中的方法,是否满足 @PointCut 的表达式,将成功匹配到的 advisor 返回

简述 Spring AOP 的设计理念和源码实现
最后就是创建代理对象,同时将找到的 advisor

设置进去

简述 Spring AOP 的设计理念和源码实现
简述 Spring AOP 的设计理念和源码实现

到此为止一个完整的代理对象就创建完毕

原文  https://juejin.im/post/5ea7eb175188256d95624f98
正文到此结束
Loading...