Spring循环依赖学习

什么是Spring循环依赖

字面意思就是A依赖B的同时B也依赖于A。

@Component
public class A {
    // A中注入了B
	@Autowired
	private B b;
}

@Component
public class B {
    // B中也注入了A
	@Autowired
	private A a;
}

什么情况下循环依赖可以被处理?

Spring解决循环依赖是有前置条件:

  1. 出现循环依赖的Bean必须要是单例
  2. 依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)

Spring是如何解决的循环依赖?

简单的循环依赖(没有AOP)

以上方简单代码A,B为例,Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建A。

Spring在创建Bean的过程中分为三步:

  • 实例化,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法

  • 属性注入,对应方法:AbstractAutowireCapableBeanFactory的populateBean方法

  • 初始化,对应方法:AbstractAutowireCapableBeanFactory的initializeBean

Debug代码流程

ClassPathXmlApplicationContext处断点

public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        A a = context.getBean("a", A.class);
        B b = context.getBean("b", B.class);
    }

请添加图片描述
请添加图片描述

请添加图片描述
此处调用getBean创建一个新的Bean。

创建A的过程实际上就是调用getBean方法,这个方法有两层含义

  • 创建一个新的Bean
  • 从缓存中获取到已经被创建的对象
    请添加图片描述
    do
    尝试从各级缓存获取A的实例bean请添加图片描述
    此处利用getSingleton方法重载,开始创建bean实例
    请添加图片描述

请添加图片描述
此处调用createBean方法,里面有个doCreateBean开始创建Bean实例。
请添加图片描述
请添加图片描述

此处使用createBeanInstance创建Bean实例。
请添加图片描述

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        Class<?> beanClass = this.resolveBeanClass(mbd, beanName, new Class[0]);
        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        } else {
            Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
            if (instanceSupplier != null) {
                return this.obtainFromSupplier(instanceSupplier, beanName);
            } else if (mbd.getFactoryMethodName() != null) {
                return this.instantiateUsingFactoryMethod(beanName, mbd, args);
            } else {
                boolean resolved = false;
                boolean autowireNecessary = false;
                if (args == null) {
                    synchronized(mbd.constructorArgumentLock) {
                        if (mbd.resolvedConstructorOrFactoryMethod != null) {
                            resolved = true;
                            autowireNecessary = mbd.constructorArgumentsResolved;
                        }
                    }
                }

                if (resolved) {
                    return autowireNecessary ? this.autowireConstructor(beanName, mbd, (Constructor[])null, (Object[])null) : this.instantiateBean(beanName, mbd);
                } else {
                    Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);
                    if (ctors == null && mbd.getResolvedAutowireMode() != 3 && !mbd.hasConstructorArgumentValues() && ObjectUtils.isEmpty(args)) {
                        ctors = mbd.getPreferredConstructors();
                        return ctors != null ? this.autowireConstructor(beanName, mbd, ctors, (Object[])null) : this.instantiateBean(beanName, mbd);
                    } else {
                        return this.autowireConstructor(beanName, mbd, ctors, args);
                    }
                }
            }
        }

前面完成了创建Bean实例,接下来调用addSingletonFactory方法,将beanA添加到三级缓存当中。
请添加图片描述

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized(this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }

        }
    }

这里只是添加了一个工厂,通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象,而这个对象实际上就是通过getEarlyBeanReference这个方法创建的。那么,什么时候会去调用这个工厂的getObject方法呢?这个时候就要到创建B的流程了。

此时开始对A进行属性注入,在注入的时候发现了A依赖于B,这个时候Spring又会去getBean(b),然后反射调用setter方法完成属性注入。

请添加图片描述

请添加图片描述

请添加图片描述
请添加图片描述

此处调用getBean给B创建一个新的Bean。请添加图片描述

请添加图片描述

利用getSingleton(beanName)给B创建Bean。此时A实例化并添加进了三级缓存,并不在一级缓存中。重复上面的步骤,将B实例化并添加到三级缓存中,
请添加图片描述

请添加图片描述
此时需要给B填充属性。

请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述
给B填充属性的过程中,调用getBean(a),此处的getBean(a)与刚开始的不同,刚开始是创建Bean,此处是从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中。
请添加图片描述
请添加图片描述
此处将放在三级缓存中的A实例放进到二级缓存当中去。
请添加图片描述

此处引用别人博客中getEarlyBeanReference的解释。
这里我们可以看出,注入到B中的A是通过getEarlyBeanReference方法提前暴露出去的一个对象,还不是一个完整的Bean,那么getEarlyBeanReference到底干了啥了,我们看下它的源码
请添加图片描述

 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
            Iterator var5 = this.getBeanPostProcessors().iterator();

            while(var5.hasNext()) {
                BeanPostProcessor bp = (BeanPostProcessor)var5.next();
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }

        return exposedObject;
    }

它实际上就是调用了后置处理器的getEarlyBeanReference,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy注解导入的AnnotationAwareAspectJAutoProxyCreator。也就是说如果在不考虑AOP的情况下,上面的代码等价于:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    return exposedObject;
}

也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP的情况下三级缓存有用嘛?讲道理,真的没什么用,我直接将这个对象放到二级缓存中不是一点问题都没有吗?如果你说它提高了效率,那你告诉我提高的效率在哪?

接着上面讲A的实例从三级缓存放入二级缓存中,回到创建B实例的过程中去,将B的实例化流程走完,此时从缓存中获取B,B不在一级缓存中,返回null,
请添加图片描述

@Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized(this.singletonObjects) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }

此时调用addSingleton方法讲B实例添加到一级缓存中去。接着去走A的流程。

请添加图片描述

接着完成A的实例化,
请添加图片描述

此时在二级缓存中找到A的实例
请添加图片描述

请添加图片描述

调用addSingleton方法讲A添加到一级缓存中去。完成A的实例化过程。
请添加图片描述

protected void addSingleton(String beanName, Object singletonObject) {
        synchronized(this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

小总结:
图解:

在这里插入图片描述
文字:
1.首先是调用getBean()方法,底层调用了doGetBean()方法,调用了getSingleton()方法从缓存中查找BeanA。
2.因为还没实例化,所以缓存中目前没有,调用getSingleton()的重载方法。
3.在getSingleton()方法中,将这个beanA的名字添加到一个集合当中(singletonsCurrentlyInCreation),标记该BeanA正在创建当中,然后回调匿名内部类的createBean()方法。
4.走到doCreateBean()方法当中,先利用反射调用构造器创建出BeanA的实例,后面会判断是否为单例(true),是否允许提前暴露引用,是否是正在创建当中(即是否在第三步的集合当中),都正确将BeanA添加到三级缓存中。
5.之后就是调用populateBean()对BeanA进行属性填充,检测到A依赖于B,就去查找B。
6.和上述查找A的方式相同,查找B,发现B又依赖于A。
7.之后就是调用getSingleton()方法去获取BeanA。依次从一级,二级,三级缓存中找Bean A,在三级缓存中找到BeanA的创建工厂,可以通过singletonFactory获取到singletonObject,singletonObject就是上面通过doCreateBean()方法实例的Bean A,此时BeanB找到BeanA的实例,将BeanA从三级缓存中移动到二级缓存中,然后调用addSingleton()方法将BeanB完直接添加到一级缓存中,完成BeanB的实例化过程。
8.最后又是BeanA继续填充属性,可以从一级缓存中获取到BeanB的实例,最后通过addSingleton()方法将BeanA从二级缓存移动到一级缓存。从而BeanA也就完成创建。

结合了AOP的循环依赖

下方是没有结合AOP的时候,通过singletonFactory.getObject();调用的getEarlyBeanReference方法。

请添加图片描述

请添加图片描述

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
            Iterator var5 = this.getBeanPostProcessors().iterator();

            while(var5.hasNext()) {
                BeanPostProcessor bp = (BeanPostProcessor)var5.next();
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }

        return exposedObject;
    }

如果在开启AOP的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreatorgetEarlyBeanReference方法,对应的源码如下:

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

回到上面的例子,我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。

在这里插入图片描述
看到这个图你可能会产生下面这些疑问

在给B注入的时候为什么要注入一个代理对象?
答:当我们对A进行了AOP代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象。

所有断点:
在这里插入图片描述

为什么Sping不选择二级缓存方式,而是要额外加一层缓存?

如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值