如何用spring源码剖析spring bean循环依赖

发布时间:2021-12-28 15:04:25 作者:柒染
来源:亿速云 阅读:198

如何用Spring源码剖析Spring Bean循环依赖

目录

  1. 引言
  2. Spring Bean的生命周期
  3. 循环依赖的定义与问题
  4. Spring中的循环依赖解决方案
  5. 源码分析
  6. 常见问题与解决方案
  7. 总结

引言

在Spring框架中,Bean的依赖注入是其核心功能之一。然而,当Bean之间存在循环依赖时,Spring如何解决这一问题成为了开发者关注的焦点。本文将深入剖析Spring源码,探讨Spring如何通过三级缓存机制解决Bean的循环依赖问题。

Spring Bean的生命周期

在深入探讨循环依赖之前,我们需要了解Spring Bean的生命周期。Spring Bean的生命周期大致可以分为以下几个阶段:

  1. 实例化:通过反射机制创建Bean的实例。
  2. 属性填充:将配置文件中定义的属性值注入到Bean实例中。
  3. 初始化:调用Bean的初始化方法(如@PostConstruct注解的方法)。
  4. 使用:Bean可以被应用程序使用。
  5. 销毁:当容器关闭时,调用Bean的销毁方法(如@PreDestroy注解的方法)。

循环依赖的定义与问题

循环依赖指的是两个或多个Bean相互依赖,形成一个闭环。例如,Bean A依赖Bean B,而Bean B又依赖Bean A。这种情况下,Spring在创建Bean时会陷入死循环,导致容器无法正常启动。

Spring中的循环依赖解决方案

Spring通过三级缓存机制解决了Bean的循环依赖问题。具体来说,Spring在Bean的创建过程中使用了三个缓存:

  1. singletonObjects:存放完全初始化好的单例Bean。
  2. earlySingletonObjects:存放提前暴露的单例Bean(尚未完成属性填充和初始化)。
  3. singletonFactories:存放Bean的工厂对象,用于创建提前暴露的Bean。

4.1 三级缓存机制

三级缓存机制的核心思想是在Bean的创建过程中,提前暴露一个尚未完全初始化的Bean实例,以便其他Bean可以引用它。具体步骤如下:

  1. 当Spring开始创建Bean A时,首先将Bean A的工厂对象放入singletonFactories缓存中。
  2. 在Bean A的属性填充阶段,如果发现需要注入Bean B,Spring会尝试从缓存中获取Bean B。
  3. 如果Bean B尚未创建,Spring会开始创建Bean B,并将Bean B的工厂对象放入singletonFactories缓存中。
  4. 在Bean B的属性填充阶段,如果发现需要注入Bean A,Spring会从singletonFactories缓存中获取Bean A的工厂对象,并创建一个提前暴露的Bean A实例。
  5. 将提前暴露的Bean A实例放入earlySingletonObjects缓存中,并注入到Bean B中。
  6. Bean B完成属性填充和初始化后,将其放入singletonObjects缓存中。
  7. 最后,Bean A完成属性填充和初始化,并将其放入singletonObjects缓存中。

4.2 提前暴露对象

提前暴露对象是解决循环依赖的关键。通过提前暴露一个尚未完全初始化的Bean实例,Spring可以打破循环依赖的死锁。

4.3 代理对象的处理

在Spring AOP中,代理对象的创建可能会影响循环依赖的解决。Spring通过SmartInstantiationAwareBeanPostProcessor接口来处理代理对象的创建,确保在循环依赖的情况下,代理对象能够正确创建和注入。

源码分析

5.1 Bean的创建过程

Spring Bean的创建过程主要在AbstractAutowireCapableBeanFactory类中实现。以下是Bean创建的主要步骤:

  1. 实例化:通过createBeanInstance方法创建Bean的实例。
  2. 属性填充:通过populateBean方法将属性值注入到Bean实例中。
  3. 初始化:通过initializeBean方法调用Bean的初始化方法。

5.2 三级缓存的实现

三级缓存的实现主要在DefaultSingletonBeanRegistry类中。以下是三级缓存的主要代码:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    // 其他代码...
}

5.3 循环依赖的解决过程

循环依赖的解决过程主要在AbstractAutowireCapableBeanFactory类的doCreateBean方法中实现。以下是主要代码:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {

    // 实例化Bean
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    final Object bean = instanceWrapper.getWrappedInstance();

    // 提前暴露Bean的工厂对象
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // 属性填充
    populateBean(beanName, mbd, instanceWrapper);

    // 初始化Bean
    exposedObject = initializeBean(beanName, exposedObject, mbd);

    return exposedObject;
}

常见问题与解决方案

6.1 构造器注入的循环依赖

构造器注入的循环依赖无法通过三级缓存机制解决,因为构造器注入要求在Bean实例化时就完成依赖注入。解决方案是使用@Lazy注解延迟加载依赖。

6.2 原型Bean的循环依赖

原型Bean的循环依赖无法通过三级缓存机制解决,因为每次获取原型Bean时都会创建一个新的实例。解决方案是尽量避免在原型Bean中使用循环依赖。

6.3 AOP代理与循环依赖

AOP代理对象的创建可能会影响循环依赖的解决。Spring通过SmartInstantiationAwareBeanPostProcessor接口确保代理对象能够正确创建和注入。

总结

Spring通过三级缓存机制成功解决了Bean的循环依赖问题。通过提前暴露尚未完全初始化的Bean实例,Spring打破了循环依赖的死锁。然而,构造器注入和原型Bean的循环依赖仍然需要开发者特别注意。通过深入理解Spring源码,开发者可以更好地应对复杂的依赖注入场景。

推荐阅读:
  1. Spring 源码(八)循环依赖
  2. spring循环依赖策略解析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring spring bean

上一篇:如何分析Apache Struts2-052 漏洞分析预警

下一篇:如何进行Apache Struts2 S2-057漏洞分析

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》