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