一、背景
我們都知道Spring可以通過xml迟螺,或者解析我們的注解,通過掃描所有資源文件,從而將所有匹配到的資源封裝成為一個BeanDefinition注冊到我們的BeanFactory中纵苛。
此時,Spring已經(jīng)知道了所有我們想要注冊到容器中的BeanDefinition,下一步就是將BeanDefinition實例化赶站,這樣才能提供出來給我們使用幔虏。
二纺念、Spring中Bean的實例化
我們發(fā)現(xiàn)Spring整個加載過程都在AbstractApplicationContext.refresh()中去完成贝椿。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing. 準備刷新
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/*
* 刷新內(nèi)部BeanFactory
* ClassPathXmlApplicationContext:1.新建BeanFactory,2.解析xml陷谱,3.封裝成BeanDefintion對象
* AnnotationConfigApplicationContext: 獲取GenericApplicationContext中的beanFactory
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 為BeanFactory進行必要的準備工作
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 進行額外的后置處理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 執(zhí)行1.BeanDefinitionResgistryPostProcessor烙博、2.BeanFactoryPostProcessor的回調(diào)
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 實例化所有實現(xiàn)了BeanPostProcessor接口的類并注冊到容器中去
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context. 國際化
initMessageSource();
// Initialize event multicaster for this context. 初始化事件類
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses. 子容器自定義實現(xiàn)
onRefresh();
// Check for listener beans and register them. 注冊事件
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//1.bean實例化,2.ioc 3.注解支持 4.BeanPostProssor執(zhí)行 5.AOP入口
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
我們著重關(guān)注一下finishBeanFactoryInitialization方法烟逊,它是Spring實例化的入口方法渣窜。
獲取BeanFactory中所有的beanDefinition名稱
合并RootBeanDefinition
非抽象的,單例的宪躯,非懶加載的就實例化
是否實現(xiàn)了FactoryBean接口乔宿,如果是加一個&前綴調(diào)用內(nèi)部的getObject,否則直接獲取
首先嘗試從緩存中獲取getSingleton(beanName),(首次獲取必然獲取不到)接著進入創(chuàng)建方法
單例創(chuàng)建之前的操作:加入到正在創(chuàng)建的一個set集合中singletonsCurrentlyInCreation
調(diào)到外部的匿名類中的實例化方法访雪,如果有值已經(jīng)創(chuàng)建成功singletonFactory.getObject();
調(diào)到doCreateBean創(chuàng)建實例BeanWrapper
允許早期引用加入單例工廠直接返回這個bean的引用详瑞。addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
填充屬性的值populateBean
initializeBean
三、Spring容器如何解決循環(huán)依賴
什么是循環(huán)依賴
循環(huán)依賴就是循環(huán)引用臣缀,就是兩個或多個 bean 相互之間的持有對方坝橡,比如 CircleA 引用 C ircleB , CircleB 引用 CircleC, CircleC 引用 CircleA,則它們最終反映為一個環(huán)精置。
@Component
public class CircleClassA {
public CircleClassA() {
System.out.println("====CircleClassA====");
}
}
@Component
public class CircleClassB {
public CircleClassB() {
System.out.println("====CircleClassB====");
}
}
首先我們需要明確的一點是:Spring只會處理上述類型的循環(huán)依賴(單例计寇,非構(gòu)造函數(shù)注入)其它情況直接報錯。
Spring在處理Bean實例化的過程中是如何解決循環(huán)依賴的呢脂倦?我們需要著重關(guān)注如下3個Map番宁。
singletonObjects
earlySingletonObjects
singletonFactories
具體步驟如下:
CircleClassA 在實例化的時候 首先從緩存中獲取不到,然后進入創(chuàng)建方法赖阻,接著將CircleClassA加入到singletonsCurrentlyInCreation中蝶押,并在singletonFactories加入一個getEarlyBeanReference,表示當前CircleClassA正在創(chuàng)建中政供。
當CircleClassA填充屬性的值populateBean時播聪,發(fā)現(xiàn)依賴了CircleClassB,觸發(fā)CircleClassB的實例化布隔。
實例化CircleClassB离陶,首先從緩存中獲取不到,然后進入創(chuàng)建方法衅檀,接著將CircleClassB加入到singletonsCurrentlyInCreation中招刨,并在singletonFactories加入一個getEarlyBeanReference,表示當前CircleClassB正在創(chuàng)建中哀军。
當CircleClassB填充屬性的值populateBean時沉眶,發(fā)現(xiàn)依賴了CircleClassA打却,觸發(fā)CircleClassA的實例化。
再次進入CircleClassA 的實例化方法谎倔,此時雖然singletonObjects中獲取不到CircleClassA柳击,但是檢測到CircleClassA存在早期暴露的實例因此嘗試從earlySingletonObjects中獲取,首次調(diào)用獲取不到從singletonFactories中獲取片习,取到之后將CircleClassA放入earlySingletonObjects捌肴,并提供給CircleClassB填充屬性的值populateBean時使用。(此時的CircleClassA只是個引用的地址藕咏,實際上并不是一個完整的CircleClassA)状知。
此時CircleClassB已經(jīng)完成了(內(nèi)部依賴的CircleClassA是個不完整的實例)并提供給CircleClassA填充屬性的值populateBean時使用。CircleClassA完成了CircleClassB的注入孽查,它變成了一個完整的實例饥悴。
又由于CircleClassB中引用了CircleClassA的一個地址。所以它也同時變成了一個完整的盲再。
實例化完成之后刪除早期引用map西设,并放入單例map中緩存singletonObjects。
程序員的核心競爭力其實還是技術(shù)洲胖,因此對技術(shù)還是要不斷的學習济榨,關(guān)注 “IT 巔峰技術(shù)” 公眾號 ,該公眾號內(nèi)容定位:中高級開發(fā)绿映、架構(gòu)師擒滑、中層管理人員等中高端崗位服務(wù)的,除了技術(shù)交流外還有很多架構(gòu)思想和實戰(zhàn)案例叉弦。
作者是 《 消息中間件 RocketMQ 技術(shù)內(nèi)幕》 一書作者丐一,同時也是 “RocketMQ 上海社區(qū)”聯(lián)合創(chuàng)始人,曾就職于拼多多淹冰、德邦等公司库车,現(xiàn)任上市快遞公司架構(gòu)負責人,主要負責開發(fā)框架的搭建樱拴、中間件相關(guān)技術(shù)的二次開發(fā)和運維管理柠衍、混合云及基礎(chǔ)服務(wù)平臺的建設(shè)。