• spring为什么要使用三级缓存来解决循环依赖


    出现循环依赖的原因

    AService依赖BService

    1. @Service("aService")
    2. public class AService {
    3. @Autowired
    4. BService bService;
    5. }

    BService依赖AService

    1. @Service("bService")
    2. public class BService {
    3. @Autowired
    4. AService aService;
    5. }

    此时就出现了循环依赖

    想要搞明白循环依赖,首先要先搞清楚bean的生命周期:SpringBean生命周期-CSDN博客

    AService Bean的创建流程

    AService创建(doCreateBean方法)的生命周期
    0. creatingSet("aService") // 将aService放入 this.singletonsCurrentlyInCreation中 表示aService正在创建中


    1.创建一个AService普通对象--->singletonFactories // 通过反射创建普通对象 然后将普通对象和BeanDefinition通过一个lambda放入singletonFactories三级缓存中(该lambda会去判断是否需要进行aop等代理操作,如果没有出现循环依赖就不需要执行lambda中的内容),用于在出现循环依赖的情况下便于二级缓存拿到普通对象

    1. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    2. isSingletonCurrentlyInCreation(beanName));
    3. if (earlySingletonExposure) {
    4. if (logger.isTraceEnabled()) {
    5. logger.trace("Eagerly caching bean '" + beanName +
    6. "' to allow for resolving potential circular references");
    7. }
    8. // 循环依赖-添加到三级缓存
    9. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    10. }


    2.填充bService属性--->去单例池中找BService对象--->创建BService的Bean对象


    3. 填充其他属性


    4.其他操怍 (完成属性依赖后,继续执行初始化前(被@PostConstruct注解修饰的方法)和初始化中(实现了InitializingBean接口并重写afterPropertiesSet())的相关操作)


    5.初始化后(AOP)--->如何判断之前是否有过aop呢? earlyProxyReferences这个map会存储执行过aop操作的bean


    5.5 在earlySingleonObjects中找到aService对象,让该对象引用完整的AService

    1. if (earlySingletonExposure) {
    2. // 会从earlyProxyReferences中获取之前创建的aService对象
    3. Object earlySingletonReference = getSingleton(beanName, false);
    4. if (earlySingletonReference != null) {
    5. if (exposedObject == bean) {
    6. // 赋值
    7. exposedObject = earlySingletonReference;
    8. }


    6.放入单例池

    BService Bean的创建流程

    1.创建一个BService普通对象


    2.填充aService属性--->去单例池中找AService对象-->creatingSet-->出现了循环依赖-去二级缓存中找->earlySingletonObjects--没找着,接着去三级缓存中去找->singletonFactories--找到后调用存放的lambda表达式->lambda-在lambda中会进行判断AService对象是否使用了aop,如果使用了则会执行代理相关操作,如果没有则不执行->AService代理对象-然后将代理的对象或者实例对象放入二级缓存中,并且删除三级缓存中的该实例->earlySingletonObjects


    3.填充其他属性


    4.其他操作


    5.初始化后


    6.放入单例池

    循环依赖情况下AService如何感知是否已经进行了aop代理操作

    在出现了循环依赖的情况下如果aService需要进行aop操作,在bService已经对aService进行了aop操作的情况下,aService在走到初始化后的aop操作时不应该进行aop操作,那么aService是如何知道bService已经对aService进行了aop操作的呢?

    1. // 两个方法都在AbstractAutoProxyCreator类中,比较两个方法可以
    2. // bService去三级缓存中调用lambda中存放的方法,在对aService执行aop相关操作之前会先把该对象存入earlyProxyReferences中
    3. @Override
    4. public Object getEarlyBeanReference(Object bean, String beanName) {
    5. Object cacheKey = getCacheKey(bean.getClass(), beanName);
    6. this.earlyProxyReferences.put(cacheKey, bean);
    7. // wrapIfNecessary执行aop相关操作
    8. return wrapIfNecessary(bean, beanName, cacheKey);
    9. }
    10. // aService初始化后 ,在执行aop相关操作之前会先判断earlyProxyReferences中是否有该bean,如果有表示执行过aop,就不需要在执行了
    11. @Override
    12. public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    13. if (bean != null) {
    14. Object cacheKey = getCacheKey(bean.getClass(), beanName);
    15. if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    16. // wrapIfNecessary执行aop相关操作
    17. return wrapIfNecessary(bean, beanName, cacheKey);
    18. }
    19. }
    20. return bean;
    21. }

    三级缓存总结

    1.单例池 singleonObjects:

    作用获取已经创建好的bean对象不需要重复生成


    2.第二级缓存 earlySingleonObjects:

    作用,再出现循环依赖的情况下,如果AService不仅依赖了BService还依赖了CService,而且CService也依赖了AService,

    如果没有二级缓存只有三级缓存时,如果是没有aop相关操作的话就没有问题,如果aop操作的话就会出现在BService获取了AService的代理对象后,CService还要重新获取AService代理对象,就会出现B和C依赖的AService不是同一个的情况,为了保证不重复去生产bean保证单例所有需要earlySingleonObjects二级缓存


    3.第三级缓存 singletonFactories:

    三级缓存的作用是让spring解决循环依赖变得更见方便一点,如果没有第三级缓存,那么当出现循环依赖时,无法拿到被依赖的对象实例。
    如果三级缓存不是使用lambda的形式,没有存入普通对象和BeanDefinition,只存了普通实例对象的话,如果该普通对象初始化后需要进行aop(需要aop就需要代理操作,进行代理操作就会产生代理对象,那么就会发生不是同一个对象的情况)操作的话又会出现问题,相似的如果存入的都是代理的对象不仅浪费极大的性能,而且bean对象也不都是需要进行aop操作的,又会出现不是同一个对象的情况

    spring三级缓存中最终打破循环的缓存就是第三级缓存

    @Async注解对循环循环依赖的影响

    1. @Service("aService")
    2. public class AService {
    3. @Autowired
    4. BService bService;
    5. @Async
    6. public void test(){
    7. System.out.println(bService.toString);
    8. }
    9. }

    如果AService存在循环依赖和aop相关操作的情况下,AService中的方法还使用了@Async异步注解,那么就会报错

     Error creating bean with name 'testBeanLifecycle': Unsatisfied dependency expressed through field 'testBeanLifecycleServiceImpl'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testBeanLifecycleServiceImpl': Bean with name 'testBeanLifecycleServiceImpl' has been injected into other beans [BServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
     

    原因是被@Async注解注释的方法和aop切入的方法相同,都有一个对应的BeanPostProcessor来处理

    1. @Override
    2. public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    3. throws BeansException {
    4. Object result = existingBean;
    5. for (BeanPostProcessor processor : getBeanPostProcessors()) {
    6. // Bean初始化后会执行所有相关的BeanPostProcessor 包括aop和@Async
    7. Object current = processor.postProcessAfterInitialization(result, beanName);
    8. if (current == null) {
    9. return result;
    10. }
    11. result = current;
    12. }
    13. return result;
    14. }

    但是和aop的postProcessAfterInitialization方法不同,@Async对应的BeanPostProcessor.postProcessAfterInitialization()并不会判断之前是否执行过代理操作,

    1. // @Async相关的BeanPostProcessor
    2. @Override
    3. public Object postProcessAfterInitialization(Object bean, String beanName) {
    4. if (this.advisor == null || bean instanceof AopInfrastructureBean) {
    5. // Ignore AOP infrastructure such as scoped proxies.
    6. return bean;
    7. }
    8. if (bean instanceof Advised) {
    9. Advised advised = (Advised) bean;
    10. if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
    11. // Add our local Advisor to the existing proxy's Advisor chain...
    12. if (this.beforeExistingAdvisors) {
    13. advised.addAdvisor(0, this.advisor);
    14. }
    15. else {
    16. advised.addAdvisor(this.advisor);
    17. }
    18. return bean;
    19. }
    20. }
    21. if (isEligible(bean, beanName)) {
    22. ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
    23. if (!proxyFactory.isProxyTargetClass()) {
    24. evaluateProxyInterfaces(bean.getClass(), proxyFactory);
    25. }
    26. proxyFactory.addAdvisor(this.advisor);
    27. customizeProxyFactory(proxyFactory);
    28. // Use original ClassLoader if bean class not locally loaded in overriding class loader
    29. ClassLoader classLoader = getProxyClassLoader();
    30. if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
    31. classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
    32. }
    33. return proxyFactory.getProxy(classLoader);
    34. }
    35. // No proxy needed.
    36. return bean;
    37. }

    所以在执行到Async对应的postProcessAfterInitialization时就会又创建一个代理对象,此时代理对象不止一个,Bean判断不是同一个Bean,并且有别的Bean依赖该Bean,抛出异常。

    1. if (earlySingletonExposure) {
    2. Object earlySingletonReference = getSingleton(beanName, false);
    3. if (earlySingletonReference != null) {
    4. // 和之前三级缓存中使用lambda表达式创建的普通bean对象不是同一个,进入else
    5. if (exposedObject == bean) {
    6. exposedObject = earlySingletonReference;
    7. }
    8. else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    9. String[] dependentBeans = getDependentBeans(beanName);
    10. Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
    11. for (String dependentBean : dependentBeans) {
    12. if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
    13. actualDependentBeans.add(dependentBean);
    14. }
    15. }
    16. if (!actualDependentBeans.isEmpty()) {
    17. // 有依赖该Bean的Bean,但是Bean不相同,抛出异常
    18. throw new BeanCurrentlyInCreationException(beanName,
    19. "Bean with name '" + beanName + "' has been injected into other beans [" +
    20. StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
    21. "] in its raw version as part of a circular reference, but has eventually been " +
    22. "wrapped. This means that said other beans do not use the final version of the " +
    23. "bean. This is often the result of over-eager type matching - consider using " +
    24. "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
    25. }
    26. }
    27. }
    28. }

    解决方式

    1. @Service("aService")
    2. public class AService {
    3. @Autowired
    4. @Lazy
    5. BService bService;
    6. @Async
    7. public void test(){
    8. System.out.println(bService.toString);
    9. }
    10. }

    给BService加上@Lazy注解,被@Lazy注解修饰的变量不会立即开始依赖注入,而是直接创建一个代理对象,在该实例被真正调用时才进行注入操作。但是此时AService已经完成了创建,不再有循环依赖的情况发生。

    构造方法注入的循环依赖

    1. @Service("aService")
    2. public class AService {
    3. BService bService;
    4. public AService(BService bService){
    5. this.bService = bService;
    6. }
    7. }
    1. @Service("bService")
    2. public class BService {
    3. AService aService;
    4. public BService (AService aService){
    5. this.aService= aService;
    6. }
    7. }

    使用构造器注入发生的循环依赖,spring本身是没有办法处理的

    因为spring Bean的创建第一步就是通过构造器来创建普通对象的,此时构造器发生了循环依赖,就没办法创建普通对象了 也就没有后续的将普通对象存入缓存的操作了。

    解决方法

    解决方法和@Async的解决方案一致,给其中一个构造器上加上@Lazy注解,直接创建代理对象就可以跳出循环依赖。

    非单例模式的循环依赖问题

    非单例模式下的循环依赖由于创建Bean的过程中不再有三级缓存,没有存放普通对象的地方,所以非单例模式下的循环依赖Spring没有办法解决。

    解决方法

    和之前一样加上@Lazy注解即可。

    1. @Service("aService")
    2. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    3. public class AService {
    4. @Autowired
    5. BService bService;
    6. }
    1. @Service("bService")
    2. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    3. public class BService {
    4. @Autowired
    5. @Lazy
    6. AService aService;
    7. }

  • 相关阅读:
    学习Bootstrap 5的第九天
    代码随想录66——额外题目【回溯、贪心】:52N皇后II、649Dota2 参议院、1221分割平衡字符串
    线性代数视频笔记
    es8316调试
    助听器不仅能帮你听到,还有另外一个功能……
    java计算机毕业设计web开发数码产品推荐平台系统设计与实现源码+mysql数据库+系统+lw文档+部署
    算法分析与设计课后练习21
    开源联合、聚力共赢丨2023 CCF中国开源大会10月开幕
    seaborn学习1
    Linux网络编程基础<多进程并发服务器>
  • 原文地址:https://blog.csdn.net/qq_45743985/article/details/134443215