定製個性化echarts 儀表盤
== 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初始化,即依賴例項被初始化
-
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
這裡貼出第二步的程式碼
/**
* 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雖然解決了迴圈依賴的初始化問題,但並不代表不會產生其他風險,編碼時需額外測試對應的邏輯,總的來說,如果可以避免迴圈依賴還是建議不用這樣做