注: 本文安裝bean生成經(jīng)過緩存的順序標(biāo)注
一級緩存 singletonObjects 緩存加載完成的bean脓恕。
二級緩存 singletonFactories 。緩存一個objectFactory工廠窿侈。
三級緩存 earlySingletonObjects 獲取到的bean炼幔。
1. 什么是spring 循環(huán)引用?
@Configurable
@ComponentScan("com.shadow")
public class Appconfig {
}
@Component
public class X {
@Autowired
Y y;
public X(){
System.out.println("X create");
}
}
@Component
public class Y {
@Autowired
X x;
public Y(){
System.out.println("Y create");
}
}
就是相互引用了對方史简,也就是我們常常的說的循環(huán)依賴.
spring 默認是支持單利的循環(huán)引用乃秀。
2. 怎么關(guān)閉spring的循環(huán)依賴?
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
支持不支持循環(huán)引用this.allowCircularReferences這個參數(shù)圆兵,可以在之前將參數(shù)修改為false;
可以修改源碼跺讯,或者使用注冊器注入;
3. spring bean 解析初始化生命周期的過程殉农;
解析.class
- 實例化一個ApplicationContext的對象刀脏;
- 調(diào)用bean工廠后置處理器完成掃描;@service,@controller, @Import,@Bean 實質(zhì)上都是工廠后置處理器的不同實現(xiàn)超凳。
- 循環(huán)解析掃描出來的類信息愈污;
- 實例化一個BeanDefinition對象來存儲解析出來的信息;
- 把實例化好的beanDefinition對象put到beanDefinitionMap當(dāng)中緩存起來聪建,以便后面實例化bean钙畔;
- 再次調(diào)用bean工廠后置處理器,這次我們程序員可以調(diào)用工廠后置處理器,在bean創(chuàng)建前再對bean的scop,class,構(gòu)造參數(shù)等進行修改金麸,spring-mybatis就是在這里擎析,將class換了;
初始化和bean生命周期
- 如果驗證完成spring在實例化一個bean之前需要推斷構(gòu)造方法,因為spring實例化對象是通過構(gòu)造方法反射揍魂,故而需要知道用哪個構(gòu)造方法桨醋;
- 推斷完構(gòu)造方法之后spring調(diào)用構(gòu)造方法反射實例化一個對象;注意我這里說的是對象现斋、對象喜最、對象;這個時候?qū)ο笠呀?jīng)實例化出來了庄蹋,但是并不是一個完整的bean瞬内,最簡單的體現(xiàn)是這個時候?qū)嵗鰜淼膶ο髮傩允菦]有注入,所以不是一個完整的bean限书;
- spring處理合并后的beanDefinition虫蝶;
- 判斷是否支持循環(huán)依賴,如果支持則提前把一個工廠存入singletonFactories——map倦西;
- 判斷是否需要完成屬性注入
- 如果需要完成屬性注入能真,則開始注入屬性
- 判斷bean的類型回調(diào)Aware接口
15:調(diào)用生命周期回調(diào)方法,在初始化前后進行調(diào)用bean的后置處理器扰柠;前面試bean工廠后置處理器粉铐;
16:如果需要代理則完成代理
17:put到單例池——bean完成——存在spring容器當(dāng)中
Spring 循環(huán)依賴的過程
- getSingleton() 嘗試去singletonObjects中獲取對象。
- 沒有卤档,則進行beforeSingletonCreate,將bean的名稱加入正在創(chuàng)建bean的集合蝙泼,表明這個bean正在創(chuàng)建;
- 通過doCreateBean創(chuàng)建對象裆装,使用反射進行創(chuàng)建踱承;
- 再次判斷是單利&支持循環(huán)依賴&正在創(chuàng)建,而此時是new 出對象了哨免,封裝到FactoryBean 對象中茎活,并將其加入 singletonFactories,二級緩存工廠;
- 這時候可以進行bean的屬性填充琢唾,進行依賴填充y類载荔,所以y也要走上面的流程,但是走到1采桃, getSingleton()是可以返回對象的懒熙,主要其中有判斷對方正在創(chuàng)建,然后從singletonFactories普办,二級緩存工廠里拿到bean; 同時將bean從二級緩存然后從singletonFactories刪除工扎,put進去三級緩存,ealySingletonObjects ,完成循環(huán)依賴注入衔蹲;
為什么首先是從三級緩存中取呢肢娘?
主要是為了性能,因為三級緩存中存的是一個x對象,如果能取到則不去二級找了橱健;
為什么一開始要存二級緩存工廠呢而钞?為什么一開始不直接存三級緩存?
如果直接存到三級緩存拘荡,只能存一個對象臼节,假設(shè)以前存這個對象的時候這對象的狀態(tài)為xa,但是我們這里y要注入的x為xc狀態(tài)珊皿,那么則無法滿足网缝;但是如果存一個工廠,工廠根據(jù)情況產(chǎn)生任意xa或者xb或者xc等等情況亮隙;比如說aop的情況下x注入y途凫,y也注入x垢夹;而y中注入的x需要加代理(aop)
為什么要從二級緩存remove溢吻?
因為如果存在比較復(fù)雜的循環(huán)依賴可以提高性能;比如x果元,y促王,z相互循環(huán)依賴,那么第一次y注入x的時候從二級緩存通過工廠返回了一個x而晒,放到了三級緩存蝇狼,而第二次z注入x的時候便不需要再通過工廠去獲得x對象了。因為if分支里面首先是訪問三級緩存倡怎;至于remove則是為了gc吧迅耘;