== Spring 循环依赖

目前Spring默认支持循环依赖,如:A依赖B的同时,B也依赖A

@Service
public class A {

    @Resource
    private B b;

    @Resource
    private BizClass bizClass;

    public BizClass getBizClass() {
        return bizClass;
    }
}
@Service
public class B implements InitializingBean, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Resource
    private A a;

    @Override
    public void afterPropertiesSet() throws Exception {
        A a = applicationContext.getBean(A.class);
        BizClass bizClass = a.getBizClass();
        System.out.println();
        //do someBiz
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


}

假设Spring先初始化A,则进入加载A的流程,先来看一下Spring初始化Bean的大体流程,由于Spring初始化过程中有诸如FactoryBean等特殊场景,较为复杂且和本话题不直接相关,下面的步骤仅为A为普通bean且为单例(singleton)的情况

初始化过程中大体步骤

版本:spring-context 5.2.2.RELEASE

首先,不管是基于注解加载还是xml配置,Spring会为每个Bean生成1个BeanDefinition,它将持有bean对应的meta信息、propertyValues(placeholder),如:

  • beanClass: A
  • scope: singleton
  • propertyValues : 通过自定义Namesapce诸如的属性
  • lazyInit
  • externallyManagedConfigMembers:外部依赖,如使用@Resource标注的变量

根据mdb初始化bean的大体流程

  • 1 doGetBean(beanName) :常用的入口

  • 2 getSingleton(baanName) :AbstractBeanFactory 先get实例,看是否已然创建,已创建就直接返回
    创建前先判断实例是否已经创建,这也是Spring规避重复创建bean的常见方式

  • 3 getSingleton(beanName,singletonFactory):AbstractBeanFactory

  • 4 |beforeSignletonCreate :DefaultSingletonBeanRegistry 表明当前bean正在create流程中 signletonsCurrentlyInCreate.add(beanName)

  • 5 createBean(baanName,mdb,args)
    createBeanInstance with BeanWrapper 创建beanWrapper 生成bean实例对象

  • 6 |addSingletonFactory(baanName,objectFactory) 注册bean对应的singletonFactory,factory实际是1个lamda表达式,getObject时可以拿到bean实例对象

  • 7 populateBean :AbstractAutowireCapableBeanFactory

  • 8 |postProcessAfterInstantiation :InstantiationAwareBeanPostProcessor autowriedAnnotation,如Autowired\Resource\Value或自定义注解 1388

  • 9 |postProcessProperties:InstantiationAwareBeanPostProcessor

  • 10 |dependenciesCheck:AbstractAutowireCapableBeanFactory

  • 11 |applyPropertyValues:AbstractAutowireCapableBeanFactory 加载runtime references

  • 12 ||resolveReference:BeanDefinitionValueResolver ,如果是RuntimeBeanReference 将会触发reference初始化,即依赖实例被初始化 ===== 触发依赖bean初始化,类似getBean效果 ======

  • 13 initializeBean:AbstractAutowireCapableBeanFactory

  • 14 |invokeAwareMethods:AbstractAutowireCapableBeanFactory 注入BeanNameAware\BeanClassLoaderAware\BeanFactoryAware

  • 15 |applyBeanPostProcessorsBeforeInitialzation:AbstractAutowireCapableBeanFactory init方法被调用前的处理,甚至可以替换spring new出的object

  • 16 |invokeInitMethods:AbstractAutowireCapableBeanFactory 调用afterPropertiesSet() 和自定义的initMethod

  • 17 |applyBeanPostProcessorsAfterInitialzation: init方法被调用后的处理,甚至可以替换spring new出的object

  • 18 registerDisposableeanIfNecessary

  • 19 afterSingletonCreate:DefaultSingletonBeanRegistry remove signletonsCurrentlyInCreate,表明当前create已结束

从上面的流程中可以看到,加载bean时可能因为postProcessor或者resolveRuntimeReference导致依赖的另1个bean初始化,如第12步

因此,循序依赖场景下初始化beanA时,由于beanA依赖了beanB,所以初始化beanA的过程中,beanB也会被初始化,而beanB也会因为依赖了beanA去尝试初始化beanA,如果不加处理就演变成了套娃

解决问题的关键在第2步,形成的依赖链 A -> B -> A 中,第二次getBean(A)时,会发现A已经在创建过程中了,会将A第一次初始化生成的singletonFactory(第6步创建的factory)拿出来,通过getObject获取到bean实例对象,并且直接返回,这样第二次getBean(A)被快速返回,且1个还没有完成初始化的对象,这也是解决循环依赖的关键:第1次getBean(A) 时创建A的实例,后续再getBean(A)时,直接返回第一次创建的对象实例,避免第二次解析A

doGetBean入口(AbstractBeanFactory)

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);               //获取已存在的bean或earlyReference(可能未完成初始化的bean)
		if (sharedInstance != null && args == null) {                 
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else { ... }                                               //如果是第一次doGetBean,则会进入正常create流程

		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isTraceEnabled()) {
					logger.trace("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

第二步的代码

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {    //第2次get时发现当前正在创建beanName流程中
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);  //获取到第1次get时创建的factory
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();                    //getObject不会new新对象,而是拿到第1次创建的bean
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;                                                       //第2次get时,返回第1次get时创建的实例,该实例还未初始化
	}

其他风险

@Service
public class B implements InitializingBean, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Resource
    private A a;

    @Override
    public void afterPropertiesSet() throws Exception {
        A a = applicationContext.getBean(A.class);
        BizClass bizClass = a.getBizClass();   //note1
        System.out.println();
        //do someBiz
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


}

note1注释的部分是存在一些隐藏风险的,在循环依赖中,我们拿到的A可能是未完成初始化的bean,甚至可能其所有成员变量都是默认值的情况,如果立即调用A的方法可能出现不可预料的错误,比如通过这种方式获取到a依赖的bizClass是会失败的

BizClass bizClass = a.getBizClass();   //note1

a虽然不为null,但是a还没有完成初始化,所以a.getBizClass()将返回null

总结

Spring虽然解决了循环依赖的初始化问题,但并不代表不会产生其他风险,编码时需额外测试对应的逻辑,总的来说,如果可以避免循环依赖还是建议不用这样做

posted on 2020-12-03 21:22  j.liu&nbsp;windliu  阅读(558)  评论(0编辑  收藏  举报