Spring源码-循环依赖

核心流程永:

A创建的时候,发现依赖了B,那就创建B.

B在创建的过程中,需要注入A,那就去单例池找A,如果找不到,那就去creatingSet中找A,如果存在A,那就说明存在循环依赖。判断A需不需要进行AOP,如果需要AOP,那就提前进行AOP;如果不需要,那就返回原始对象。

如果提前AOP,那就创建代理对象,并将创建出来的代理对象放到二级缓存中,二级缓存保证了代理对象只会被创建一次。

三级缓存

  1. singletonObjects 单例池(完整Bean生命周期)
  2. earlySingletonObjects (半成品)早期的单例池(普通对象或者代理对象),存放到是三级缓存执行后的结果
  3. singletonFactory (ObjectFactory)临时存放Lambda表达式<普通对象>,可以生成代理对象,放到二级缓存(生成后就清空)

使用到的核心方法:

1.addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)):三级缓存存放beanName和lambda表达式,lambda表达式存放原始对象,并且通过该lambda表达式可以生成AOP的代理对象

2.getSingleton():找bean--涉及一级缓存、二级缓存、三级缓存

说明:@EnableAOP就是生成一个BeanPostProcessor,然后AOP依赖BeanPostProcessor来生成代理对象

例子:

A a= new A();

a.b = b;

B b = new B();

b.a = a;

说明:

1.A在创建的时候,在属性填充的时候,会调用getSingleton找B,此时B还没有创建,那这个方法就直接返回,去创建B

2.B在属性填充的时候,也会进到这个方法(getSingleton),去一级缓存,二级缓存拿,拿不到就加锁,去三级缓存拿,拿到lambda表达式,执行lambda表达式,拿到A的原始对象或者创建A的代理对象或者,完成B的创建,将B放到单例池。

3.然后A从单例池拿到B,完成后续创建流程。

如果不存在代理的逻辑,那么两级缓存就能解决循环依赖的问题

如果在初始化后要进行AOP,那么就需要在初始化后生成代理对象,如果存在循环依赖的问题,就需要提前进行AOP。

我们怎么知道出现了循环依赖呢?

在一开始就记录了creatingSet<>(正在创建中的bean)

核心代码如下:

// 为了解决循环依赖提前缓存单例创建工厂
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                  isSingletonCurrentlyInCreation(beanName));// isSingletonCurrentlyInCreation正在创建中
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                         "' to allow for resolving potential circular references");
        }
        // 循环依赖-添加到三级缓存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

1.Lambda表达式的执行

DefaultSingletonBeanRegistry#getSingleton

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 一级缓存没有
        singletonObject = this.earlySingletonObjects.get(beanName);// 去二级缓存拿
        if (singletonObject == null && allowEarlyReference) { // 二级缓存没有 加锁,去三级缓存找
            synchronized (this.singletonObjects) { // 加锁
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();// 执行三级缓存的lambda表达式
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);// 用完 lambda表达式就移除
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

执行lambda表达式,那就会调用getEarlyBeanReference(在类AbstractAutoProxyCreator)

在这个方法中,earlyProxyReferences.put:记录是否进行了AOP,如果进行了AOP,就放到这个Map中

然后调用wrapIfNecessary:判断是否需要AOP,需要就createProxy(创建代理对象)

正常进行AOP的地方(初始化后):AbstractAutoProxyCreator#postProcessAfterInitialization()

如果提前进行了AOP,那么这个方法不会再AOP,直接返回原始对象;之后去二级缓存找到代理对象,完成A的创建。

 

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {// 如果AOP后发现bean发生改变
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

2.几种循环依赖的情况

Enable注解就是生成一个BeanPostProcessor(不完全正确),比如@EnableTransactional没有新生成beanPostProcessor,添加的是Advisor。

2.1@Async会导致暴露的对象改变

if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) { // 如果有些postProcessor直接创建了bean,那这里就不一样
            exposedObject = earlySingletonReference;
        }
        else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            // beanName被哪些bean依赖了,现在发现beanName所对应的bean对象发生了改变,那么则会报错
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                    actualDependentBeans.add(dependentBean);
                }
            }
            if (!actualDependentBeans.isEmpty()) {
                throw new BeanCurrentlyInCreationException(beanName,
                       "Bean with name '" + beanName + "' has been injected into other beans [" +
                       StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                       "] 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.");
            }
        }
    }
}

(AOP和@Async都存在的情况下,并且存在循环依赖,那就会抛异常)

@Async的大致执行原理:创建代理对象,异步开一个线程去完成操作的。和AOP是不同的

@EnableAsync就会新增一个postProcessor,这个postProcessor在AOP之后执行

Async会生成一个新的代理对象,那么此时exposedObject就发生改变了。这样就会导致赋值给其他Bean的和放到单例池的不是同一个Bean

解决办法:加了lazy注解之后,会给bService直接生成一个代理对象给A赋值。之后真正用到bService的时候,才会去找A,或者创建B的bean,比如这里的test用到bService

说明:A中加Async导致出错,是因为A依赖B,需要创建B,B在创建的时候用到了A的代理对象,赋值给B的这个代理对象和A最终生成的对象(A最终放到单例池的对象)不是同一个,所以报错

说明:把Async写到bService,就不会报错,此时是因为,B需要拿到完整的A,拿到A之后,就可以进行初始化、AOP以及Async,之后会生成完整的B。

2.2两个原型Bean产生的循环依赖

两个都是原型bean,那他们产生的循环依赖没有办法解决;如果只有一个原型Bean,那么不会报错

两个原型bean的原因:他们会 互相 一直创建彼此,导致死循环

2.3实例化使用构造方法产生的循环依赖

解决办法:通过@Lazy注解来解决,Spring会创建一个aService的代理对象来给B的构造方法使用

2.4@Transactional

不会报错,@Transactional因为没有新生成beanPostProcessor,添加的是Advisor,所以最后暴露的对象是同一个

2.5自己注入自己

不会出现问题

执行流程:

1.实例化A,存到三级缓存

2.注入A,先去一级缓存找A,再去二级缓存找A,再去三级缓存找A,此时肯定能找到A(或者是A的普通对象,或者是A的代理对象),那么完成A的注入

3.继续创建,完成A的创建

Spring循环依赖是指两个或多个Bean之间相互依赖,形成一个循环引用的关系。在Spring容器启动的过程中,如果存在循环依赖,那么默认情况下会抛出BeanCurrentlyInCreationException异常。 下面是Spring循环依赖源码分析: 1. 当我们向Spring容器中注入一个Bean时,Spring会先检查这个Bean是否已经正在创建中(正在创建的Bean是无法注入的),如果正在创建中,则直接返回一个早期引用,否则继续创建Bean。 2. 在Bean的创建过程中,当遇到依赖注入(如@Autowired注解)时,Spring会检查要注入的Bean是否已经在创建中。如果是,则返回一个代理对象作为占位符,等待真正的Bean创建完毕后再进行注入。 3. Spring使用三级缓存来解决循环依赖问题。第一级缓存是单例池,存放已经创建好的单例Bean。第二级缓存是提前暴露的ObjectFactory,存放早期引用。第三级缓存是用于存放正在创建中的Bean的缓存,用于检测循环依赖。 4. 当检测到循环依赖时,Spring会尝试使用构造函数的方式完成循环依赖。它会先创建一个空对象,并将其放入到第三级缓存中。然后调用构造函数去创建这个Bean,此时依赖的Bean会返回一个早期引用。最后,将这个Bean加入到第一级缓存中,并开始注入依赖。 5. 当所有的Bean都创建完成后,Spring会触发后置处理器的回调方法,完成Bean的初始化。 总结:Spring循环依赖通过使用三级缓存和构造函数来解决,在Bean创建过程中动态地判断和处理循环依赖关系,确保所有的Bean都能被正确地创建和注入。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值