循环依赖,是依赖关系形成了一个圆环。比如:A对象有一个属性B,那么这时候我们称之为A依赖B,如果这时候B对象里面有一个属性A。那么这时候A和B的依赖关系就形成了一个循环,这就是所谓的循环依赖。如果这时候IOC容器创建A对象的时候,发现B属性,然后创建B对象,发现里面有A属性,然后创建B.....这么无限循环下去。我们先用代码演示一下:
public class A { private B b=new B(); } public class B { private A a=new A(); } public class Test { public static void main(String[] args) { A a = new A(); } }
运行一下结果
那么我们可以看到循环依赖存在的问题了
显然这种思路是不正确的。
如果我们自己写的话,该如何解决的呢?
public class A { private B b; public void setB(B b) { this.b = b; } } public class B { private A a; public void setA(A a) { this.a = a; } } public class Test { public static void main(String[] args) { A a = new A();//创建a对象 B b = new B();//因为a对象依赖B,那么创建B b.setA(a);//创建B对象的时候,发现依赖A,那么把通过构造方法生成的对象a赋值给B a.setB(b);//然后把生成的b对象注入到a里面 } }
当使用Spring的 @Autowired 注解的时候,其实Spring的实现原理和上面很相似,先通过生成相关的对象,然后再把里面需要依赖的对象设置进去。
我们现在从Spring源码来走一遍。。
我们现贴出最基本的测试代码
@Component public class A { @Autowired B b; } @Component public class B { @Autowired A a; } public class RecyclerTest { @Test public void test() { ApplicationContext context = new AnnotationConfigApplicationContext("com.kailaisi.demo.recycler"); //getbean得时候才进行IOC容器中的对象的实例化工作 A a = (A) context.getBean("a"); } }
在我们之前发布的 SpringBoot启动流程源码分析 里面,我们提到过bean单例的生成是在Spring容器创建过程中来完成的。经过多层的调用,最终会调用到 doGetBean 这个方法里面。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ... Object bean; //先从缓存中获取是否定义了对应的类,这里的缓存包括了半成品类缓存(只生成了类,但是还没有进行属性注入的类)和成品类缓存(已经完成了属性注入的类) Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { ...... //如果符合条件,直接从对饮给的bean单例中获取到对象,然后返回 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { ... try { ..... //创建单例bean,解决循环依赖的根本方案 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { //调用创建单例的方法 return createBean(beanName, mbd, args); } ... }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } ... return (T) bean; } @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { ... //进行bean的创建 Object beanInstance = doCreateBean(beanName, mbdToUse, args); ... } protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { //bean的包装类 BeanWrapper instanceWrapper = null; ... if (instanceWrapper == null) { //创建beanDefinition所对应的的参数的bean实例,这里通过构造方法或者工厂方法或者cglib创建了对象 instanceWrapper = createBeanInstance(beanName, mbd, args); } if (earlySingletonExposure) { //将对象放到registeredSingletons队列中,并从earlySingletonObjects中移除 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); ... //注入A的依赖,这里面会发现属性,然后从doGetBean()方法开始,生成B对象,然后循环走到这里的时候,在队列里面会同时存在A对象和B对象。然后B对象注入A成功,返回后将生成的B注入到A,此时完成了A和B的对象生成,并解决了循环依赖问题 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } ... }
加载过程比较长,其实主要是在加载的过程中将对象的创建过程进行了分类处理,在创建的不同时期,放入到队列来进行区分。
知道了这几个队列以后,我们可以来整理测试例子中,A和B对象是如何一步步创建,并解决其循环依赖的问题了。
上述就是spring解决循环依赖的整体过程,跟我们之前的那个方法很相似,只是对于各种情况的处理更仔细。而且从这个过程也能理解spring对于对象的创建过程。
本文由 开了肯 发布!