关于spring:Spring-动态代理时是如何解决循环依赖的为什么要使用三级缓存

23次阅读

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

前言

在钻研『Spring 是如何解决循环依赖的』的时候,理解到 Spring 是借助 三级缓存 来解决循环依赖的。

同样在上一节留下了疑难:

  1. 循环依赖为什么要应用三级缓存?而不是应用二级缓存?
  2. AOP 动静代理对循环依赖的有没有什么影响?

本篇文章也是围绕下面的内容进行开展。

笔记也在一直整顿,之前可能会有点芜杂。

循序渐进,看一看什么是循环依赖?

开始先简略回顾一下 Bean 的创立过程,当然小伙伴也能够间接浏览『单例 Bean 的创立』这篇文章。

不过思考到浏览本文前再浏览上一篇文章、Debug 等等,会比拟耗时,所以本篇文章后面一小部分会先对之前的文章内容做简要概括,也相当于对我本人学习的常识进行一个总结。

先来回顾一下三级缓存的概念。

singletonObjects: 一级缓存,存储单例对象,Bean 曾经实例化,初始化实现。

earlySingletonObjects: 二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。

singletonFactories: 三级缓存,存储 singletonFactory。

Bean 的创立过程

@Service
public class CircularServiceA {private String fieldA = "字段 A";}

通过下面的流程,能够看出 Spring 在创立 Bean 的过程中重点是在 AbstractAutowireCapableBeanFactory 中的以下三个步骤:

  1. 实例化 createBeanInstance: 其中实例化 Bean 并对 Bean 进行赋值,像例子中的 fieldA 字段在这里就会赋值。
  2. 属性注入 populateBean: 能够了解为对 Bean 外面的属性进行赋值。(会依赖其余 Bean)
  3. 初始化 initializeBean: 执行初始化和 Bean 的后置处理器。

实例化赋值源码能够浏览:

BeanUtils.instantiateClass(constructorToUse)

如果要依赖其余 Bean 呢?

那如果 CircularServiceA 依赖了其余 Bean 呢?

@Service
public class CircularServiceA {

    private String fieldA = "字段 A";

    @Autowired
    private CircularServiceB circularServiceB;

}
@Service
public class CircularServiceB {}

当 A 依赖了 B 的时候,在 createBeanInstance 这一步,并不会对 B 进行属性赋值。

而是在 populatedBean 这里查找依赖项,并创立 B。

循环依赖下的创立过程

循环依赖的场景,在上一篇文章曾经有所解说,这里仅仅画图阐明一下。

@Service
public class CircularServiceA {

    private String fieldA = "字段 A";

    @Autowired
    private CircularServiceB circularServiceB;

}
@Service
public class CircularServiceB {
    @Autowired
    private CircularServiceA circularServiceA;
}

在 A 和 B 循环依赖的场景中:

B populatedBean 查找依赖项 A 的时候,从一级缓存中尽管未获取到 A,然而发现 A 在创立中。

此时,从三级缓存中获取 A 的 singletonFactory 调用工厂办法,创立 getEarlyBeanReference A 的晚期援用并返回。

B 援用到 A,B 就能够初始化结束,而后 A 同样也能够初始化结束了。

二级缓存是否解决循环依赖

通过下面的图,仔细分析一下,其实把二级缓存拿掉,在 B 尝试获取 A 的时候间接返回 A 的实例,是不是也是能够的?

答案是:能够的!

然而为什么还是用三级缓存呢?

网上的很多材料说是和动静代理有关系,那就从动静代理的方面持续往下剖析剖析。

动静代理的场景

在 JavaConfig(配置类)上增加 @EnableAspectJAutoProxy 注解,开启 AOP,通过 Debug 循序渐进看一看动静代理对循环依赖的影响。

动静代理下,Bean 的创立过程

@Service
public class CircularServiceA {
    private String fieldA = "字段 A";

    public void methodA() {System.out.println("办法 A 执行");

    }
}
@Aspect
@Component
public class AspectA {@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
    public void beforeA() {System.out.println("beforeA 执行");
    }
}

只有 A 的状况下,给 A 增加切面,开始 Debug。

后面的流程都雷同,在 initializeBean 开始呈现差别。

这一步须要初始化 Bean 并执行 Bean 的后置处理器。

其中有一个处理器为:AnnotationAwareAspectJAutoProxyCreator 其实就是加的注解切面,会跳转到 AbstractAutoProxyCreator 类的 postProcessAfterInitialization 办法

如图所示:wrapIfNecessary 办法会判断是否满足代理条件,是的话返回一个代理对象,否则返回以后 Bean。

后续调用 getProxy 、createAopProxy 等等,最终执行到上面一部分。

最终会执行到这里,AOP 代理相干的就不细看了。

一路放行,直到 initializeBean 执行完结。

此时发现:A 被替换为了代理对象。

所以 doCreateBean 返回,以及前面放到一级缓存中的都是代理对象。

有循环依赖的动静代理

这一次把循环依赖关上:

@Service
public class CircularServiceA {

    private String fieldA = "字段 A";

    @Autowired
    private CircularServiceB circularServiceB;

    public void methodA() {System.out.println("办法 A 执行");

    }
}
@Aspect
@Component
public class AspectA {@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
    public void beforeA() {System.out.println("beforeA 执行");

    }

}
@Service
public class CircularServiceB {

    @Autowired
    private CircularServiceA circularServiceA;

    public void methodB() {}
}
@Aspect
@Component
public class AspectB {@Before("execution(public void com.liuzhihang.circular.CircularServiceB.methodB())")
    public void beforeB() {System.out.println("beforeB 执行");

    }

}

开始 Debug,后面的一些列流程,都和失常的没有什么区别。而惟一的区别在于,创立 B 的时候,须要从三级缓存获取 A。

此时在 getSingleton 办法中会调用:singletonObject = singletonFactory.getObject();

有时会比拟纳闷 singletonFactory.getObject() 调用的是哪里?

所以这一块调用的是 getEarlyBeanReference,开始遍历执行 BeanPostProcessor

看到 wrapIfNecessary 就明确了吧!这块会获取一个 代理对象

也就是说此时返回,并放到二级缓存的是一个 A 的代理对象。

这样 B 就创立结束了!

到 A 开始初始化并执行后置处理器了!因为 A 也有代理,所以 A 也会执行到 postProcessAfterInitialization 这一部分!

然而在执行 wrapIfNecessary 之前,会先判断代理对象缓存是否有 A 了。

this.earlyProxyReferences.remove(cacheKey) != bean

然而这块获取到的是 A 的代理对象。必定是 false。所以不会再生成一次 A 的代理对象。

总结

能够看到,循环依赖下,有没有代理状况下的区别就在:

singletonObject = singletonFactory.getObject();

在循环依赖产生的状况下 B 中的 A 赋值时:

  1. 无代理:getObject 间接返回原来的 Bean
  2. 有代理:getObject 返回的是代理对象

而后都放到 二级缓存

为什么要三级缓存?

  1. 假如去掉三级缓存

去掉三级缓存之后,Bean 间接创立 earlySingletonObjects,看着如同也能够。

如果有代理的时候,在 earlySingletonObjects 间接放代理对象就行了。

然而会导致一个问题:在实例化阶段就得执行后置处理器,判断有 AnnotationAwareAspectJAutoProxyCreator 并创立代理对象

这么一想,是不是会对 Bean 的生命周期有影响。

同样,先创立 singletonFactory 的益处就是:在真正须要实例化的时候,再应用 singletonFactory.getObject() 获取 Bean 或者 Bean 的代理。相当于是提早实例化。

  1. 假如去掉二级缓存

如果去掉了二级缓存,则须要间接在 singletonFactory.getObject() 阶段初始化结束,并放到一级缓存中。

那有这么一种场景,B 和 C 都依赖了 A。

要晓得在有代理的状况下 singletonFactory.getObject() 获取的是代理对象。

而屡次调用 singletonFactory.getObject() 返回的代理对象是不同的,就会导致 B 和 C 依赖了不同的 A。

那如果获取 B 到之后间接放到一级缓存,而后 C 再获取呢?

???? ……

一级缓存放的是曾经初始化结束的 Bean,要晓得 A 依赖了 B 和 C,A 这时候还没有初始化结束。

小结

循环依赖的场景有很多,本文只是通过 Debug,来理解到循环依赖和 AOP 之间的关系,以及理解到为什么要用三级缓存。

当然,Spring 设计之初是什么样子的?如何一步一步倒退成当初这种的?

必定是不能缓缓去钻研了,所以只能以当初的版本,去推测作者的用意。

不足之处,多多斧正。

相干举荐

  • Spring 是如何解决循环依赖的?
  • Spring 源码学习 16:单例 Bean 创立
  • Spring 源码学习 15:finishBeanFactoryInitialization(重点)

正文完
 0