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

前言

不管是采用 XML 仍是注解方式,最终 Spring 读取加载后都会生成与之对应的 BeanDefinition,而后利用它就能够去实例化一个对象。java

BeanDefinition 用来描述建立一个实例所须要的信息express

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

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

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

示例代码

基本用法这里就再也不赘述框架

装载 BeanDefinition

调用 BeanFactoryProcessor

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

而后调用 refresh() 方法中,会调用 invokeBeanFactoryPostProcessors(beanFactory); 在这个方法中去调用全部的 BeanFactoryProcessorspa

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

经过实现了 BeanFactoryProcessor 咱们能够拿到 ConfigurableListableBeanFactory,经过容器咱们就能拿到 BeanDefinition 进行分析和修改,从而间接的影响 Bean 的实例化作到咱们无感知,无侵入的加强和拓展类的功能code

调用 BeanDefinitionRegistryPostProcessor

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

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

实例化

找到全部的 @Aspect

要想应用 advice 逻辑,首先咱们须要找到对应的切面,而后咱们在其中查看须要在哪些方法中织入对应的 advice 逻辑,好比在 UserServiceImplqueryUser 这个 JoinPoint 切点以后织入 afterQuery

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

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

它有它的做用好比

  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 存入代理对象中,最后容器中最后的实现类也变成了对应的代理类

建立代理对象

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 的机会

在这个方法中会去获取全部的 BeanPostProcessor 而后依次调用其 postProcessAfterInitialization 方法

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

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

最后就是建立代理对象,同时将找到的 advisor 设置进去

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

经过代理对象执行 advice 逻辑

  1. 首先咱们以前设置好的 advice 方法做为一个调用链 chain
  2. 根据这些参数建立 CglibMethodInvocation 而后调用 proceed
  3. 随后就会按照已经排好的顺序执行对应的 advice 方法和执行被代理的目标方法

总结

总的来讲就分为 2 步骤

  • 建立代理对象
    • 根据类和注解或者 XML 建立 BeanDefinition
    • 根据 BeanDefinition 找到全部的 advice 方法
    • 建立 Bean 对象
    • 判断对象是否须要被代理(是否知足 @PointCut)中的表达式条件
      • 知足的话就建立代理对象而且将 advice 等信息进行缓存
      • 不知足的话就跳过
  • 调用 advice 逻辑和目标方法
相关文章
相关标签/搜索