一、背景
我們都知道Spring可以通過xml,或者解析我們的注解,通過掃描所有資源文件,從而將所有匹配到的資源封裝成為一個BeanDefinition注冊到我們的BeanFactory中。
此時,Spring已經知道了所有我們想要注冊到容器中的BeanDefinition,下一步就是將BeanDefinition實例化,這樣才能提供出來給我們使用。
二、Spring中Bean的實例化
我們發現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.
/*
* 刷新內部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.
// 執行1.BeanDefinitionResgistryPostProcessor、2.BeanFactoryPostProcessor的回調
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 實例化所有實現了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. 子容器自定義實現
onRefresh();
// Check for listener beans and register them. 注冊事件
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//1.bean實例化,2.ioc 3.注解支持 4.BeanPostProssor執行 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();
}
}
我們著重關注一下finishBeanFactoryInitialization方法,它是Spring實例化的入口方法。
獲取BeanFactory中所有的beanDefinition名稱
合并RootBeanDefinition
非抽象的,單例的,非懶加載的就實例化
是否實現了FactoryBean接口,如果是加一個&前綴調用內部的getObject,否則直接獲取
首先嘗試從緩存中獲取getSingleton(beanName),(首次獲取必然獲取不到)接著進入創建方法
單例創建之前的操作:加入到正在創建的一個set集合中singletonsCurrentlyInCreation
調到外部的匿名類中的實例化方法,如果有值已經創建成功singletonFactory.getObject();
調到doCreateBean創建實例BeanWrapper
允許早期引用加入單例工廠直接返回這個bean的引用。addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
填充屬性的值populateBean
initializeBean
三、Spring容器如何解決循環依賴
什么是循環依賴
循環依賴就是循環引用,就是兩個或多個 bean 相互之間的持有對方,比如 CircleA 引用 C ircleB , CircleB 引用 CircleC, CircleC 引用 CircleA,則它們最終反映為一個環。
@Component
public class CircleClassA {
public CircleClassA() {
System.out.println("====CircleClassA====");
}
}
@Component
public class CircleClassB {
public CircleClassB() {
System.out.println("====CircleClassB====");
}
}
首先我們需要明確的一點是:Spring只會處理上述類型的循環依賴(單例,非構造函數注入)其它情況直接報錯。
Spring在處理Bean實例化的過程中是如何解決循環依賴的呢?我們需要著重關注如下3個Map。
singletonObjects
earlySingletonObjects
singletonFactories
具體步驟如下:
CircleClassA 在實例化的時候 首先從緩存中獲取不到,然后進入創建方法,接著將CircleClassA加入到singletonsCurrentlyInCreation中,并在singletonFactories加入一個getEarlyBeanReference,表示當前CircleClassA正在創建中。
當CircleClassA填充屬性的值populateBean時,發現依賴了CircleClassB,觸發CircleClassB的實例化。
實例化CircleClassB,首先從緩存中獲取不到,然后進入創建方法,接著將CircleClassB加入到singletonsCurrentlyInCreation中,并在singletonFactories加入一個getEarlyBeanReference,表示當前CircleClassB正在創建中。
當CircleClassB填充屬性的值populateBean時,發現依賴了CircleClassA,觸發CircleClassA的實例化。
再次進入CircleClassA 的實例化方法,此時雖然singletonObjects中獲取不到CircleClassA,但是檢測到CircleClassA存在早期暴露的實例因此嘗試從earlySingletonObjects中獲取,首次調用獲取不到從singletonFactories中獲取,取到之后將CircleClassA放入earlySingletonObjects,并提供給CircleClassB填充屬性的值populateBean時使用。(此時的CircleClassA只是個引用的地址,實際上并不是一個完整的CircleClassA)。
此時CircleClassB已經完成了(內部依賴的CircleClassA是個不完整的實例)并提供給CircleClassA填充屬性的值populateBean時使用。CircleClassA完成了CircleClassB的注入,它變成了一個完整的實例。
又由于CircleClassB中引用了CircleClassA的一個地址。所以它也同時變成了一個完整的。
實例化完成之后刪除早期引用map,并放入單例map中緩存singletonObjects。
程序員的核心競爭力其實還是技術,因此對技術還是要不斷的學習,關注 “IT 巔峰技術” 公眾號 ,該公眾號內容定位:中高級開發、架構師、中層管理人員等中高端崗位服務的,除了技術交流外還有很多架構思想和實戰案例。
作者是 《 消息中間件 RocketMQ 技術內幕》 一書作者,同時也是 “RocketMQ 上海社區”聯合創始人,曾就職于拼多多、德邦等公司,現任上市快遞公司架構負責人,主要負責開發框架的搭建、中間件相關技術的二次開發和運維管理、混合云及基礎服務平臺的建設。