从源码解决Spring 循环依赖问题

通过构造函数配置循环引用

Class B 需要通过构造函数注入实现 class C,而 class C 需要通过构造函数注入实现 class B.如果为 classes B 和 C 配置 beans 以相互注入,则 Spring IoC 容器会在运行时检测到此循环 reference,并抛出BeanCurrentlyInCreationException

创建类B

public class B {

    private C ccc;

    public B(C ccc){
        System.out.println("创建一个对象: 【"+this + "】");
    }

}

创建类C

public class C {
    private B bbb;
    public C(B bbb){
        System.out.println("创建一个对象: 【"+this + "】");
    }


    public void setBbb(B bbb) {
        this.bbb = bbb;
    }
}

通过构造注入


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bBean" class="com.sean.spring.bean.B">
        <constructor-arg ref="cBean"/>
    bean>
    <bean id="cBean" class="com.sean.spring.bean.C">
        <constructor-arg ref="bBean"/>
    bean>

beans>

测试类

    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring/application-context.xml");
        B bean  = context.getBean(B.class);
        logger.debug(bean);
    }

抛出异常
在这里插入图片描述

通过Set注入循环引用

如果我们不使用构造器注入,而使用setter注入就不会出现循环依赖报错问题。

setter注入

B类,创建一个set方法

public class B {

    private C ccc;

    public void setCcc(C ccc) {
        this.ccc = ccc;
    }
}

C类

public class C {
    private B bbb;

    public void setBbb(B bbb) {
        this.bbb = bbb;
    }
}

通过setter配置bean XML文件


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bBean" class="com.sean.spring.bean.B">
        <property name="ccc" ref="cBean"/>
    bean>
    <bean id="cBean" class="com.sean.spring.bean.C">
        <property name="bbb" ref="bBean"/>
    bean>

beans>

测试通过了,打印了我们创建的B对象
从源码解决Spring 循环依赖问题_第1张图片
如果我们通过setter注入,指定scope="prototype",循环依赖也会报错。

总结:同样对于循环依赖的场景,构造器注入和prototype类型的属性set注入都会初始化Bean失败。因为默认是单例的,所以单例的属性注入是可以成功的。

下边我们就从源码中去求证,Spring Bean 的加载流程程序入口

ApplicationContext context = new ClassPathXmlApplicationContext("spring/application-context.xml");
B bean  = context.getBean(B.class);

第二行getBean已经看到可以获取Bean对象,那么肯定在第一步就完成了Bean的加载。

ClassPathXmlApplicationContext

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
    	//设置父级的ApplicationContext,这里是null,没什么用
        super(parent);
        //存储Spring配置文件的路径到本地,这里取的就是"spring/application-context.xml"
        this.setConfigLocations(configLocations);
        if (refresh) {
        	//Spring Bean的核心方法,用于刷新Spring上下文信息,定义了上下文加载的过程
            this.refresh();
        }

    }

Spring源码比较复杂也比较多,所以我们只关注需要注意的点即可,不然很容易迷失。Spring Bean 核心方法 refresh方法

    public void refresh() throws BeansException, IllegalStateException {
    	//加锁,避免多线程场景下同时刷新Spring上下文
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            //这是在子类中启动refreshBeanFactory的地方
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
            	//设置BeanFactory的后置处理
                this.postProcessBeanFactory(beanFactory);
                //调用BeanFactory的后处理器,这些后处理器在Bean定义中向容器注册
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //注册Bean的后处理器,在Bean创建过程中调用
                this.registerBeanPostProcessors(beanFactory);
                //对上下文消息源进行初始化
                this.initMessageSource();
                //初始化上下文的事件机制
                this.initApplicationEventMulticaster();
                //初始化其他特殊的Bean
                this.onRefresh();
                //检查监听Bean并且将这些Bean向容器注册
                this.registerListeners();
                //实例化所有的单列
                this.finishBeanFactoryInitialization(beanFactory);
                //发布容器事件,结束refresh过程
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }
				//异常时,销毁Bean
                this.destroyBeans();
                //重置"active"标志
                this.cancelRefresh(var9);
                throw var9;
            } finally {
            	//清空缓存
                this.resetCommonCaches();
            }

        }
    }

obtainFreshBeanFactory 方法作用就是获取刷新Spring上下文Bean工厂,我们看他里边的refreshBeanFactory方法

    protected final void refreshBeanFactory() throws BeansException {
        //如果已经存在直接销毁并关闭工厂
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
        	//创建Ioc容器,使用的是DefaultListableBeanFactory 
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
            //启动对BeanDefintion的载入
            this.loadBeanDefinitions(beanFactory);
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

从以上源码可以看到,创建Bean工厂的时候。其实是创建的DefaultListableBeanFactory 工厂对象,这也是我们要讨论的循环依赖的关键。

Spring Bean的定义 BeanDefinition

接下来我们简单介绍一下loadBeanDefinitions
Spring Ioc容器的两大步骤就是Bean的定义初始化,它是先定义,然后初始化和依赖注入的。

Bean的定义分为三步

  1. Resource定位:这步是Sring Ioc容器根据开发者的配置,进行资源定位,在Spring开发中,通过XML或者注解配置,定位的内容也是在配置文件汇总提供的
  2. BeanDefinition的载入:这个时候只是将Resources定位到的信息保存到Bean定义(BeanDefinition)中,此时不会创建Bean的实例。
  3. BeanDefinition的注册,这个过程是将BeanDefinition的信息发布到Spring Ioc容器,注意,这时候仍旧没有对应的Bean的实例创建。

loadBeanDefinitions在这里主要做了这么几件事:

  1. 初始化了BeanDefinitionReader
  2. 通过BeanDefinitionReader获取Resource,也就是xml配置文件的位置,并且把文件转换成一个叫Document的对象
  3. 接下来需要将Document对象转化成容器内部的数据结构(也就是BeanDefinition),也即是将Bean定义的List、Map、Set等各种元素进行解析,转换成Managed类(Spring对BeanDefinition数据的封装)放在BeanDefinition中;这个方法是RegisterBeanDefinition(),也就是解析的过程。
  4. 解析完成后,会把解析的结果放到BeanDefinition对象中并设置到一个Map中

以上就是Spring Ioc中Bean定义过程,如何定位,载入和注册的有兴趣的读者可以去看一下源码。可查看PropertiesBeanDefinitionReader

再回到refresh方法,在创建完beanFactory后,执行了prepareBeanFactory方法,这个方法做bean的准备工作。
中间有一段代码是这样的

        if (!beanFactory.containsLocalBean("systemProperties")) {
            beanFactory.registerSingleton("systemProperties", this.getEnvironment().getSystemProperties());
        }

意思就是说要判断我们本地配置的bean是否要注册为单例的,进入这个方法
registerSingleton,我们会看到他有两个实现类DefaultListableBeanFactoryDefaultSingletonBeanRegistry,通过之前的refreshBeanFactory方法,我们已经知道创建的beanFactory是DefaultListableBeanFactory ,那DefaultSingletonBeanRegistry很显然是单例时候创建的beanFactory,而Spring Ioc默认是单例模式。DefaultSingletonBeanRegistry方法就是我们苦苦找寻的关键

DefaultSingletonBeanRegistry

	//存储单例Bean名称,单例Bean实现映射关系
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    //存储Bean名称,ObjectFactory实现映射关系
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    //存储Bean名称,预加载Bean实现映射关系
    private final Map<String, Object> earlySingletonObjects = new HashMap(16);

DefaultSingletonBeanRegistry维护了三个Map,为什么我会专门列出它们,下边会用到。

再回到refresh方法,bean准备完成,后续就是设置后置处理器等一些初始化操作,我们重点看一下finishBeanFactoryInitialization方法,实例化所有的non-lazy-init,里边有一个preInstantiateSingletons方法,顾名思义就是实例化所有的单例bean。

    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }

        List<String> beanNames = new ArrayList(this.beanDefinitionNames);
        Iterator var2 = beanNames.iterator();

        while(true) {
            while(true) {
                String beanName;
                RootBeanDefinition bd;
                do {
                    do {
                        do {
                            if (!var2.hasNext()) {
                                var2 = beanNames.iterator();

                                while(var2.hasNext()) {
                                    beanName = (String)var2.next();
                                    Object singletonInstance = this.getSingleton(beanName);
                                    if (singletonInstance instanceof SmartInitializingSingleton) {
                                        final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
                                        if (System.getSecurityManager() != null) {
                                            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                                                public Object run() {
                                                    smartSingleton.afterSingletonsInstantiated();
                                                    return null;
                                                }
                                            }, this.getAccessControlContext());
                                        } else {
                                            smartSingleton.afterSingletonsInstantiated();
                                        }
                                    }
                                }

                                return;
                            }

                            beanName = (String)var2.next();
                            //将Spring上下文的AbstractBeanDefinition转换为RootBeanDefinition
                            bd = this.getMergedLocalBeanDefinition(beanName);
                        } while(bd.isAbstract());
                    } while(!bd.isSingleton());
                } while(bd.isLazyInit());

                if (this.isFactoryBean(beanName)) {
                	//实例化的关键
                    final FactoryBean<?> factory = (FactoryBean)this.getBean("&" + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = (Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            public Boolean run() {
                                return ((SmartFactoryBean)factory).isEagerInit();
                            }
                        }, this.getAccessControlContext());
                    } else {
                        isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
                    }
					//isEagerInit是否需要立即加载
                    if (isEagerInit) {
                        this.getBean(beanName);
                    }
                } else {
                    this.getBean(beanName);
                }
            }
        }
    }

IoC容器的依赖注入

getBean >>>doGetBean >>>createBean >>>doCreateBean

createBean不但生成了bean,还对bean进行了处理,比如实现了BeanDefinition中的init-method属性定义,Bean后置处理器等。

createBean

       try {
           return AbstractBeanFactory.this.createBean(beanName, mbd, args);
       } catch (BeansException var2) {
           AbstractBeanFactory.this.destroySingleton(beanName);
           throw var2;
       }

doCreateBean

        if (mbd.isSingleton()) {
            instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
        }

        if (instanceWrapper == null) {
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);
        }

与依赖注入关系特别密切的方法有createBeanInstance()populateBean()方法,createBeanInstance()中生成了Bean所包含的java对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些方式都是由相关的BeanDefinition来指定的。

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    	//确认需要创建的Bean实例的类可以初始化
        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 if (mbd.getFactoryMethodName() != null) {
        	//这里使用工厂方法对Bean进行实例化
            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) {
            	//使用默认的构造函数对Bean进行实例化
                return autowireNecessary ? this.autowireConstructor(beanName, mbd, (Constructor[])null, (Object[])null) : this.instantiateBean(beanName, mbd);
            } else {
            	//使用构造函数进行实例化
                Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);
                return ctors == null && mbd.getResolvedAutowireMode() != 3 && !mbd.hasConstructorArgumentValues() && ObjectUtils.isEmpty(args) ? this.instantiateBean(beanName, mbd) : this.autowireConstructor(beanName, mbd, ctors, args);
            }
        }
    }

instantiateBean 调用 instantiate 方法

beanInstance = this.getInstantiationStrategy().instantiate(mbd, beanName, this);

instantiate

    public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        if (bd.getMethodOverrides().isEmpty()) {
            Constructor constructorToUse;
            synchronized(bd.constructorArgumentLock) {
                constructorToUse = (Constructor)bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }

                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = (Constructor)AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[])null);
                                }
                            });
                        } else {
                            constructorToUse = clazz.getDeclaredConstructor((Class[])null);
                        }

                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    } catch (Exception var9) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", var9);
                    }
                }
            }
			//BeanUtils进行实例化,反射
            return BeanUtils.instantiateClass(constructorToUse, new Object[0]);
        } else {
        	//使用CGLIB实例化
            return this.instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

要了解怎样使用cglib来生成Bean对象,需要看一下SimpleInstantiationStrategy类。这个Strategy是Spring用来生成Bean对象的默认类,它提供了两种实例化Java对象的方法,一种是通过BeanUtils,它使用了JDK的反射功能,一种是通过前面提到的CGLIB来生成, 使用SimpleInstantiationStrategy生成Java对象。

以上分析了实例化bean的整个过程,但是并没有完成整个依赖注入过程,只是实例化了对象,重要的属性的处理过程还需看populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
		//这里取得在BeanDefinition 中设置的property值
        PropertyValues pvs = mbd.getPropertyValues();
        if (bw == null) {
            if (!((PropertyValues)pvs).isEmpty()) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
        } else {
            boolean continueWithPropertyPopulation = true;
            if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
                Iterator var6 = this.getBeanPostProcessors().iterator();

                while(var6.hasNext()) {
                    BeanPostProcessor bp = (BeanPostProcessor)var6.next();
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;
                        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                            continueWithPropertyPopulation = false;
                            break;
                        }
                    }
                }
            }
			
			//开始进行依赖注入过程
            if (continueWithPropertyPopulation) {
                if (mbd.getResolvedAutowireMode() == 1 || mbd.getResolvedAutowireMode() == 2) {
                    MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs);
                    //根据Bean的名字
                    if (mbd.getResolvedAutowireMode() == 1) {
                        this.autowireByName(beanName, mbd, bw, newPvs);
                    }
					
					//根据Bean的类型
                    if (mbd.getResolvedAutowireMode() == 2) {
                        this.autowireByType(beanName, mbd, bw, newPvs);
                    }

                    pvs = newPvs;
                }

                boolean hasInstAwareBpps = this.hasInstantiationAwareBeanPostProcessors();
                boolean needsDepCheck = mbd.getDependencyCheck() != 0;
                if (hasInstAwareBpps || needsDepCheck) {
                    PropertyDescriptor[] filteredPds = this.filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                    if (hasInstAwareBpps) {
                        Iterator var9 = this.getBeanPostProcessors().iterator();

                        while(var9.hasNext()) {
                            BeanPostProcessor bp = (BeanPostProcessor)var9.next();
                            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;
                                pvs = ibp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);
                                if (pvs == null) {
                                    return;
                                }
                            }
                        }
                    }

                    if (needsDepCheck) {
                        this.checkDependencies(beanName, mbd, filteredPds, (PropertyValues)pvs);
                    }
                }

				//对属性进行注入
                this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs);
            }
        }
    }

Autowire的实现

Spring Ioc提供自动装配的方式,使用反射自动查找属性的类型或者名字,然后基于属性的类型或名字来自动匹配Ioc容器中的Bean

autowire属性是在对Bean属性进行依赖注入时起作用。对autowire属性进行处理,从而完成对Bean属性的自动依赖装配,是在populateBean中实现的。对属性autowire的处理是populateBean处理过程的一个部分。在populateBean的实现中,在处理一般的Bean之前,先对autowire属性进行处理。如果当前的Bean配置了byName和byType属性,那么调用相应的autowireByName方法和autowireByType方法。例如,对于byName,它首先通过反射机制从当前Bean中得到需要注入的属性名,然后使用这个属性名向容器申请与之同名的Bean,这样实际又触发了另一个Bean的生成和依赖注入的过程。

对autowireByName来说,它首先需要得到当前Bean的属性名,这些属性名已经在BeanWrapper和BeanDefinition中封装好了,然后是对这一系列属性名进行匹配的过程。在匹配的过程中,因为已经有了属性的名字,所以可以直接使用属性名作为Bean名字向容器索取Bean,这个getBean会触发当前Bean的依赖Bean的依赖注入,从而得到属性对应的依赖Bean。在执行完这个getBean后,把这个依赖Bean注入到当前Bean的属性中去,这样就完成了通过这个依赖属性名自动完成依赖注入的过程。

    protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        String[] propertyNames = this.unsatisfiedNonSimpleProperties(mbd, bw);
        String[] var6 = propertyNames;
        int var7 = propertyNames.length;

        for(int var8 = 0; var8 < var7; ++var8) {
            String propertyName = var6[var8];
            if (this.containsBean(propertyName)) {
            	//使用当前bean的属性名作为bean的名字
                Object bean = this.getBean(propertyName);
                pvs.add(propertyName, bean);
                this.registerDependentBean(propertyName, beanName);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Added autowiring by name from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
                }
            } else if (this.logger.isTraceEnabled()) {
                this.logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName + "' by name: no matching bean found");
            }
        }

    }

Spring创建好了BeanDefinition之后呢,会开始实例化Bean,并且对Bean的依赖属性进行填充。实例化时底层使用了CGLIB或Java反射技术,最终是对属性的注入,整个流程代码较多,总结下来就是下面整个流程图

从源码解决Spring 循环依赖问题_第2张图片

下边我们来看循环依赖问题

之前得到的结论:

  1. 通过set注入,能够解决循环依赖,可以初始化

  2. 构造器注入,或者非单例注入,不能解决循环依赖,不可以被初始化,抛出异常。

下边我们就重点关注一下从doGetBeanpopulateBean的过程。因为代码众多,我这里把主要调用的几个关键方法做成流程图。
从源码解决Spring 循环依赖问题_第3张图片
我们主要看getSingleton方法,可以看到获取单例bean的时候会判断三个Map是否已经存在,这其实就是本节的关键,下边具体会讲到。

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    	//通过beanName在已经创建的Singleton集合中查找
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            synchronized(this.singletonObjects) {
            	//如果没找到,就去earlySingletonObjects中查找
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                	//还没找到,就去singletonFactory 找
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                    	//找到后放到earlySingletonObjects中
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        //将singletonFactories中的缓存删除
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

        return singletonObject != NULL_OBJECT ? singletonObject : null;
    }

执行addSingletonFactory,此时bean已经初始化,只是还没执行populateBean方法,重要的值还没初始化放进去,但是Spring已经认为它是可复用的bean,所以看以下代码,Spring会把bean放到三级缓存中。

    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);
            }

        }
    }

populateBean中有autowireByNameautowireByType方法,无论是通过name还是type注入的,最终结果都是,如果B中注入了一个C,那么执行B中的populateBean方法时,就会调用getBean©,先把C初始化完成之后,再接着初始化B。
单例循环注入B-C-B,B中依赖C,C又依赖B

  1. 先初始化B
  2. doGetBean(B)
  3. getSingleton(B),因为这时候B第一次初始化,肯定没有缓存
  4. instantiateBean(B),因为B是通过set属性关联的,所以初始化B时并不会触发C的初始化
  5. addSingletonFactory,此时即将初始化,但还没有设置值(populateProperty)的B放到三级缓存singletonFactory
  6. populateBean(B),因为B中设置了C,那么会触发getBean( C)
  7. doGetBean( C)
  8. getSingleton©,因为这时候C第一次初始化,所以没有缓存办法get到
  9. instantiateBean( C)
  10. addSingletonFactory,同样的C也放到三级缓存singletonFactory
  11. populateBean( C),因为C中设置了B,那么将触发getBean(B)
  12. 再次触发doGetBean(B)
  13. getSingleton(B),因为这时候B已经是第二次初始化,直接从三级缓存singletonFactory中拿到之前放进去的值
  14. 拿到缓存之后直接返回

本质就是在在13步的时候拿到了三级缓存,结束了循环的getBean,解决了循环依赖问题

因为也就印证了我们上边的第一条结论:1.通过set注入,能够解决循环依赖,可以初始化

至于第二条结论:2.构造器注入,或者非单例注入,不能解决循环依赖,不可以被初始化,抛出异常。

其实这个也很好理解了,还记得instantiateBeaninstantiate(实例化)其实就是一个new对象的过程,new对象要执行构造器,因为B中构造器注入了C,那么B在instantiate实例化时,就直接进行了C的实例化,那么B在关键的方法addSingletonFactory()之前就去初始化了C,导致三级缓存中根本就没有B,所以发生了死循环,Spring发现后就抛出了异常。

原型模式Bean的初始化

       try {
           this.beforePrototypeCreation(beanName);
           prototypeInstance = this.createBean(beanName, mbd, args);
       } finally {
           this.afterPrototypeCreation(beanName);
       }
    protected void beforePrototypeCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        if (curVal == null) {
            this.prototypesCurrentlyInCreation.set(beanName);
        } else if (curVal instanceof String) {
            Set<String> beanNameSet = new HashSet(2);
            beanNameSet.add((String)curVal);
            beanNameSet.add(beanName);
            this.prototypesCurrentlyInCreation.set(beanNameSet);
        } else {
            Set<String> beanNameSet = (Set)curVal;
            beanNameSet.add(beanName);
        }

    }

beforePrototypeCreation会把每个正在创建的prototype类型的beanName放到set中。并且在dogetBean中会检查beanName是否处于创建状态,如果是则抛出异常

对原型模式的beanName进行检查

      if (this.isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

上一步已经把beanName放到Set中,这里判断Set包含,返回布尔类型

    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
        Object curVal = this.prototypesCurrentlyInCreation.get();
        return curVal != null && (curVal.equals(beanName) || curVal instanceof Set && ((Set)curVal).contains(beanName));
    }

并且这个校验还在createBean之前没从流程来看,无论是构造注入还是设值注入,第二次注入同一个bean的时候执行到这里,一定会校验抛出异常,因为不能完成注入,也就不能循环依赖。

总结:Spring在InstantiateBean时执行构造器方法,构造出实例,如果是单例的话,会将它放入一个singletonBeanFactory的缓存中,再进行populateBean方法,设置属性。通过一个singletonBeanFactory的缓存解决了循环依赖的问题。

补充

网上也有博客通过一道算法解释了循环依赖的真谛,这个算法就是经典的“两数之和”
从源码解决Spring 循环依赖问题_第4张图片
下边给出解决方法

import java.util.HashMap;
import java.util.Map;
class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i< nums.length; i++){
            int complement = target - nums[i];
            if (map.containsKey(complement)){
                return new int[]{map.get(complement),i};
            }
            map.put(nums[i], i);
        }
        throw new IllegalArgumentException("No two sum solution"); 
    }
}

同样是定义一个Map集合,先去map中找需要的值,没有的话就将当前数字放到map中,如果找到就一起返回。

和我们所讲的三级缓存,先去缓存找bean,没有就实例化,放到map,如果有需要就冲map中取到。

好了,本篇文章到此就结束了,因为spring源码比较复杂繁多,笔者可能也有理解不到位的地方,欢迎各位指教。

你可能感兴趣的:(Spring)