前言
本章主要內(nèi)容是由以下部分組成,
- Spring 中容器初始化入口
- 以最經(jīng)典的ClassPathXmlApplicationContext 為例,講解Spring IoC 的容器初始化過程
在學習源碼的過程當中,我想強調(diào)兩點:
- 一定要學會抓重點僻他,歸納核心類、核心方法、核心步驟坤候。
- 理解類、變量企蹭、方法名的命名白筹,Spring 源碼的命名是很講究的,很多時候是自解釋的
- 一定要學會看Java doc 谅摄,同上徒河,這種頂級的框架的java doc 描述非常的詳盡
Spring 容器初始化入口
啟動容器,實際上指的就是實例化ApplicationContext的這個動作送漠。只是在不同情況下可能有不同的表現(xiàn)形式顽照。
- ClassPathXmlApplicationContext 通過XML配置
ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml");
- AnnotationConfigApplicationContext 通過java config 類配置
@Configuration
@ComponentScan("ric.study.demo.ioc")
public class BeanDemoConfig {
public static void main(String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(BeanDemoConfig.class);
System.out.println("Spring container started and is ready");
...
}
}
類似前面這兩種new ***ApplicationContext
的方式,很少會用于直接的生產(chǎn)開發(fā)。一般都是我們自己在demo中或者單元測試中會用到代兵。
- WebApplicationContext SpringMVC
這個實際上是我們平常最常用的初始化方式尼酿,Spring MVC 中 ServletContext 為 Spring 的 IoC容器提供了宿主環(huán)境。是通過ContextLoaderListener 的初始化來建立的植影。
WebApplicationContext 的初始化調(diào)用鏈路:ContextLoaderListener.contextInitialized --> ContextLoader.initWebApplicationContext --> ContextLoader.createWebApplicationContext --> ContextLoader.determineContextClass --> ContextLoader.determineContextClass裳擎。
底層是通過反射來實例化的。
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
這塊內(nèi)容先簡要提一下思币,屬于SpringMVC的內(nèi)容鹿响,不是我們今天要講的Spring IoC 模塊的知識。
容器初始化 源碼解析
現(xiàn)在讓我們正式開始的源碼解讀谷饿。會從最經(jīng)典的ClassPathXmlApplicationContext 上下文為起點惶我,來描述整個過程。
在說明之前各墨,我想了想還是覺得把整個IoC容器初始化的關(guān)鍵步驟為大家梳理一下指孤,以便于大家能在心里有個大概的脈絡(luò),更容易讀懂源碼贬堵,更容易抓住重點恃轩。再重復(fù)提一句,看源碼一定要學會抓重點黎做,歸納核心類叉跛、核心方法、核心步驟蒸殿。
ClassPathXmlApplicationContext 的容器初始化我們大致分為下面幾步:
-
BeanDefinition 的 Resource 定位
這里的Resource定位 是通過繼承ResourceLoader 獲得的筷厘,ResourceLoader代表了加載資源的一種方式,正是策略模式的實現(xiàn)宏所。
從 Resource中解析酥艳、載入BeanDefinition
BeanDefinition 在IoC 容器中的注冊
前面說了,實例化這個上下文爬骤,就是在啟動 IoC 容器充石。那我們肯定要從它的構(gòu)造函數(shù)入手。
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
入?yún)⒅械?code>configLocations在這里就是你XML配置文件 的 classpath霞玄。
setConfigLocations(configLocations);
我這里不展開講骤铃,內(nèi)容不復(fù)雜,就是把一些帶有占位符的地址解析成實際的地址坷剧。
再之后就是refresh()
惰爬,我們說的容器初始化,就是在這里面進行的惫企,這里取名為refresh撕瞧,是因為容器啟動之后,再調(diào)用refresh()
會刷新IoC 容器。
這里先放上IoC容器初始化的時序圖风范,方便理解咨跌,
refresh() 的源碼:
@Override
public void refresh() throws BeansException, IllegalStateException {
// 來個鎖,不然 refresh() 還沒結(jié)束硼婿,你又來個啟動或銷毀容器的操作锌半,那不就亂套了嘛
synchronized (this.startupShutdownMonitor) {
// 準備工作,記錄下容器的啟動時間寇漫、標記“已啟動”狀態(tài)刊殉、處理配置文件中的占位符
prepareRefresh();
// 這步比較關(guān)鍵,這步完成后州胳,配置文件就會解析成一個個 Bean 定義记焊,注冊到 BeanFactory 中,
// 當然栓撞,這里說的 Bean 還沒有初始化遍膜,只是配置信息都提取出來了,
// 注冊也只是將這些信息都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 設(shè)置 BeanFactory 的類加載器瓤湘,添加幾個 BeanPostProcessor瓢颅,手動注冊幾個特殊的 bean
// 這塊待會會展開說
prepareBeanFactory(beanFactory);
try {
// 【這里需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現(xiàn)了此接口弛说,
// 那么在容器初始化以后挽懦,Spring 會負責調(diào)用里面的 postProcessBeanFactory 方法∧救耍】
// 這里是提供給子類的擴展點信柿,到這里的時候,所有的 Bean 都加載醒第、注冊完成了渔嚷,但是都還沒有初始化
// 具體的子類可以在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實現(xiàn)類或做點什么事
postProcessBeanFactory(beanFactory);
// 調(diào)用 BeanFactoryPostProcessor 各個實現(xiàn)類的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊 BeanPostProcessor 的實現(xiàn)類,注意看和 BeanFactoryPostProcessor 的區(qū)別
// 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 兩個方法分別在 Bean 初始化之前和初始化之后得到執(zhí)行稠曼。注意形病,到這里 Bean 還沒初始化
registerBeanPostProcessors(beanFactory);
// 初始化當前 ApplicationContext 的 MessageSource,國際化不是重點蒲列,不展開
initMessageSource();
// 初始化當前 ApplicationContext 的事件廣播器,這里也不展開了
initApplicationEventMulticaster();
// 從方法名就可以知道搀罢,典型的模板方法(鉤子方法)蝗岖,
// 具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh();
// 注冊事件監(jiān)聽器,監(jiān)聽器需要實現(xiàn) ApplicationListener 接口榔至。這也不是我們的重點,過
registerListeners();
// 重點,重點筝闹,重點
// 初始化所有的 singleton beans
//(lazy-init 的除外)
finishBeanFactoryInitialization(beanFactory);
// 最后,廣播事件划提,ApplicationContext 初始化完成
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.
// 銷毀已經(jīng)初始化的 singleton 的 Beans,以免有些 bean 會一直占用資源
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
我會從上述流程中邢享,挑以下幾個進行分析鹏往,
- prepareRefresh() 創(chuàng)建容器前的準備工作
- obtainFreshBeanFactory() 創(chuàng)建 BeanFactory
- prepareBeanFactory(beanFactory) 對BeanFactory進行一些特征的設(shè)置工作
- finishBeanFactoryInitialization(beanFactory); 初始化所有的 singleton beans(DI的入口)
1. prepareRefresh() 創(chuàng)建容器前的準備工作
protected void prepareRefresh() {
// 記錄啟動時間,
// 將 active 屬性設(shè)置為 true骇塘,closed 屬性設(shè)置為 false伊履,它們都是 AtomicBoolean 類型
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
initPropertySources();
// 校驗 xml 配置文件
getEnvironment().validateRequiredProperties();
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
2.★ obtainFreshBeanFactory() 創(chuàng)建 Bean 容器,加載并注冊 Bean
IoC初始化里面最重要的部分款违。
關(guān)鍵是以下幾步唐瀑,
- 初始化BeanFactory
- 加載Bean
- 注冊Bean
- ...
注意:這步完成后,Bean 并沒有完成初始化插爹,實際的實例并沒有被創(chuàng)建哄辣。
源碼位置:AbstractApplicationContext#obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 關(guān)閉舊的 BeanFactory (如果有),創(chuàng)建新的 BeanFactory赠尾,加載 Bean 定義力穗、注冊 Bean 等等
refreshBeanFactory();
// 返回上一步剛剛創(chuàng)建的BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
源碼位置:AbstractRefreshableApplicationContext#refreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
// 如果 ApplicationContext 已經(jīng)加載過 BeanFactory,銷毀所有的Bean萍虽,關(guān)閉BeanFactory
// 注意點:應(yīng)用中BeanFactory是可以有多個的睛廊,這里可不是說全局是否有BeanFactory
// 而是說當前的ApplicationContext有沒有BeanFactory!
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化一個 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 用于 BeanFactory 的序列化杉编,一般人應(yīng)該用不到吧...
beanFactory.setSerializationId(getId());
// 下面是兩個重點方法
// 設(shè)置 BeanFactory 的兩個重要屬性
// 是否允許 Bean 覆蓋超全、是否允許循環(huán)引用 TODO 2.1
customizeBeanFactory(beanFactory);
// 加載BeanDefinition到BeanFactory 單獨拉出來講
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
看到這里的時候,可以感覺到一個設(shè)計思路邓馒,ApplicationContext 繼承自 BeanFactory嘶朱,但是它不應(yīng)該被理解為 BeanFactory 的實現(xiàn)類,而是說其內(nèi)部持有一個實例化的 BeanFactory(DefaultListableBeanFactory)光酣。以后所有的 BeanFactory 相關(guān)的操作其實是委托給這個實例來處理的疏遏。
2.1 customizeBeanFactory
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
BeanDefinition 的覆蓋問題可能會有開發(fā)者碰到這個坑,就是在配置文件中定義 bean 時使用了相同的 id 或 name救军,默認情況下财异,allowBeanDefinitionOverriding 屬性為 null(Boolean類型),如果在同一配置文件中重復(fù)了唱遭,會拋錯戳寸,但是如果不是同一配置文件中,會發(fā)生覆蓋拷泽。
循環(huán)引用也很好理解:A 依賴 B疫鹊,而 B 依賴 A袖瞻。或 A 依賴 B拆吆,B 依賴 C聋迎,而 C 依賴 A。
默認情況下枣耀,Spring 允許循環(huán)依賴霉晕,當然如果你在 A 的構(gòu)造方法中依賴 B,在 B 的構(gòu)造方法中依賴 A 是不行的奕枢。
2.2 ★ loadBeanDefinitions(beanFactory) 加載BeanDefinition
看下這個方法的聲明娄昆,
/**
* Load bean definitions into the given bean factory, typically through
* delegating to one or more bean definition readers.
* @param beanFactory the bean factory to load bean definitions into
* @throws BeansException if parsing of the bean definitions failed
* @throws IOException if loading of bean definition files failed
* @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*/
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
在ClassPathXmlApplicationContext 是按照解析XML的加載方式》毂颍看javadoc的描述萌焰,是通過XmlBeanDefinitionReader
來載入Bean Definitions。
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// 初始化Reader 不重要谷浅,看下這個方法的javadoc就很好理解了
initBeanDefinitionReader(beanDefinitionReader);
// 真正重要的步驟0歉!
// 用Reader去加載XML配置
loadBeanDefinitions(beanDefinitionReader);
}
loadBeanDefinitions(beanDefinitionReader)
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* 看這句注釋:this method is just supposed to load and/or register bean definitions.
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// 這個分支一疯,通過路徑名去獲取Resource撼玄,會和上面的方法殊途同歸
reader.loadBeanDefinitions(configLocations);
}
}
AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
// 遍歷解析XML文件,加載 BeanDefinition
counter += loadBeanDefinitions(resource);
}
return counter;
}
接下去的源碼不細講墩邀,這里載入分為兩大步掌猛,
- 一就是通過調(diào)用XML的解析器獲取到 document 對象,完成通用XML解析眉睹;
- 二就是按照Spring的Bean規(guī)則進行解析荔茬。Spring的Bean規(guī)則進行解析這個過程是
BeanDefinitionDocumentReader
來實現(xiàn)的,里面包含了各種Spring Bean定義規(guī)則的處理竹海。
這里我覺得核心知識點就是Spring Bean規(guī)則的解析慕蔚,簡單點來說,里面包含了我們在XML配置的那些信息斋配,怎么解析成容器中 BeanDefinition的規(guī)則和步驟孔飒。這部分由于和主要流程關(guān)系不大,我就沒貼源碼解析了艰争,會占掉很大的篇幅坏瞄,影響閱讀和理解。
在這因為Spring 的 Bean配置方式有很多甩卓,解析配置信息到BeanDefinition的實現(xiàn)方式也有很多鸠匀,XML又是現(xiàn)在少用的方式,所以關(guān)于XML中的Spring Bean規(guī)則的解析的詳細源碼就先略過了猛频。有興趣的同學可以閱讀《Spring 技術(shù)內(nèi)幕》這本書或者其他的文章書籍狮崩。
2.3 注冊Bean
雖然上面說了不講XML 解析 成 BeanDefinition的過程源碼。但是上述loadBeanDefinitions(resource)
包含了我們關(guān)鍵的第三步鹿寻,注冊Bean睦柴。這部分還是需要填一下的。
注意一下前面實例化Reader的代碼毡熏,
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
/**
* Create new XmlBeanDefinitionReader for the given bean factory.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
*/
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
beanDefinitionReader 獲取到 beanFactory 的引用坦敌,這個引用會在beanDefinition 被加載完畢要注冊的時候使用到×》ǎ可以看到是因為BeanDefinitionRegistry
這個接口狱窘,賦予了BeanFactory注冊BeanDefinition的特性。
具體執(zhí)行“注冊Bean”這一動作的源碼财搁,按照上述loadBeanDefinitions(resource)
方法一直走下去的話是在DefaultBeanDefinitionDocumentReader#processBeanDefinition()
方法中蘸炸,
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 注冊Bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
源碼位置BeanDefinitionReaderUtils#registerBeanDefinition()
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 注冊
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 如果還有別名的,把別名全都注冊一遍
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 到時候獲取的時候尖奔,就是先把Alias轉(zhuǎn)化成BeanName搭儒,再去獲取對應(yīng)的Bean
registry.registerAlias(beanName, alias);
}
}
}
上面的registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
,
源碼位置DefaultListableBeanFactory#registerBeanDefinition()
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 注意哦,這里是有關(guān) “允許Bean覆蓋” 的邏輯代碼
// 記得這個配置 allowBeanDefinitionOverriding
BeanDefinition oldBeanDefinition;
// beanDefinitionMap 是存放所有BeanDefinition的容器
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// not null 說明提茁,有重復(fù)名稱的bean
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
// 判斷是否允許覆蓋淹禾,不允許直接拋異常
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// 打下debug log...用框架定義的 Bean 覆蓋用戶自定義的 Bean
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
// 打下debug log...用新的 Bean 覆蓋舊的 Bean
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
// 打下debug log...用同等的 Bean 覆蓋舊的 Bean
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 覆蓋了
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 判斷是否有其他Bean已經(jīng)開始初始化了
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 檢測創(chuàng)建 Bean 階段已經(jīng)開啟,需要對 beanDefinitionMap 進行并發(fā)控制
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
// 最最最正常的分支
// 注冊 到 容器中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 這是一個 ArrayList茴扁,所以會按照 bean 配置的順序保存每一個注冊的 Bean 的名字
this.beanDefinitionNames.add(beanName);
// 這是個 LinkedHashSet铃岔,代表的是手動注冊的 singleton bean,
// 注意這里是 remove 方法峭火,到這里的 Bean 當然不是手動注冊的
// 手動指的是通過調(diào)用以下方法注冊的 bean :
// registerSingleton(String beanName, Object singletonObject)
// 這不是重點毁习,解釋只是為了不讓大家疑惑。Spring 會在后面"手動"注冊一些 Bean躲胳,
// 如 "environment"蜓洪、"systemProperties" 等 bean,我們自己也可以在運行時注冊 Bean 到容器中的
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
以上坯苹,只是obtainFreshBeanFactory()
的內(nèi)容隆檀,到這里,BeanFactory也算是實例化完成了粹湃。
這里還是來個分割線恐仑。因為接下去會講refresh() 方法的后續(xù)步驟的知識點,我想讀者同學在這里最好最好为鳄,回到 前面refresh()
總述的部分裳仆,再看一下。(如果你是一邊還打開著IDE孤钦,在對照閱讀調(diào)試的話歧斟,回到最前面refresh() 方法纯丸,再繼續(xù)往下)
3. prepareBeanFactory(beanFactory)
此方法負責對BeanFactory進行一些特征的設(shè)置工作,這些特征在代碼中都有體現(xiàn)静袖。
/**
* Configure the factory's standard context characteristics,
* such as the context's ClassLoader and post-processors.
* @param beanFactory the BeanFactory to configure
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
// BeanFactory 需要加載類觉鼻,所以需要獲得類加載器
// 設(shè)置當前ApplicationContext的類加載器
beanFactory.setBeanClassLoader(getClassLoader());
// 內(nèi)含 Spel 解釋器,暫時不重要
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 注冊屬性編輯器队橙,暫時不重要
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// 添加一個ApplicationContextAwareProcessor坠陈,主要針對實現(xiàn)了Aware接口的Bean
// 延伸知識:在Spring中我們自己的bean可以通過實現(xiàn)EnvironmentAware等一系列Aware接口,獲取到Spring內(nèi)部的一些對象捐康。
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 依賴解析忽略, 設(shè)置哪些接口在進行依賴注入的時候應(yīng)該被忽略
// 通俗來說仇矾,下面幾行的意思就是,如果某個 bean 依賴于以下幾個接口的實現(xiàn)類解总,在自動裝配的時候忽略它們贮匕,
// Spring 會通過其他方式來處理這些依賴。
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
/**
* 下面幾行就是為特殊的幾個 bean 賦值花枫,如果有 bean 依賴了以下幾個粗合,會注入這邊相應(yīng)的值,
* 之前我們說過乌昔,"當前 ApplicationContext 持有一個 BeanFactory"隙疚,這里解釋了第一行
* ApplicationContext 還繼承了 ResourceLoader、ApplicationEventPublisher磕道、MessageSource
* 所以對于這幾個依賴供屉,可以賦值為 this,注意 this 是一個 ApplicationContext
* 那這里怎么沒看到為 MessageSource 賦值呢溺蕉?那是因為 MessageSource 被注冊成為了一個普通的 bean
*/
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
// 這個 BeanPostProcessor 也很簡單伶丐,在 bean 實例化后,如果是 ApplicationListener 的子類疯特,
// 這個postProcessor的作用就是將其添加到 listener 列表中哗魂,可以理解成:注冊 事件監(jiān)聽器
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 這里涉及到特殊的 bean录别,名為:loadTimeWeaver邻吞,AspectJ相關(guān)內(nèi)容
// 不是這里的重點组题,放過我
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
// Spring 的“智能”操作,會幫我們默認注冊一些有用的Bean
// 如果沒有定義 "environment" 這個 bean崔列,那么 Spring 會 "手動" 注冊一個
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
// 如果沒有定義 "systemProperties" 這個 bean旺遮,那么 Spring 會 "手動" 注冊一個
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
// 如果沒有定義 "systemEnvironment" 這個 bean赵讯,那么 Spring 會 "手動" 注冊一個
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
4. ★ finishBeanFactoryInitialization(beanFactory) 實例化所有單例
這里會負責初始化所有的 singleton beans盈咳。
Spring 會在這個階段完成所有的 singleton beans 的實例化猪贪。
到目前為止讯私,應(yīng)該說 BeanFactory 已經(jīng)創(chuàng)建完成西傀,并且所有的實現(xiàn)了 BeanFactoryPostProcessor
接口的 Bean 都已經(jīng)初始化并且其中的 postProcessBeanFactory(factory)
方法已經(jīng)得到回調(diào)執(zhí)行了。而且 Spring 已經(jīng)“手動”注冊了一些特殊的 Bean娘锁,如 environment
莫秆、systemProperties
等悔详。
剩下的就是初始化 singleton beans 了茄螃,我們知道它們是單例的,如果沒有設(shè)置懶加載用狱,那么 Spring 會在接下來初始化所有的 singleton beans拼弃。
源碼位置:AbstractApplicationContext#finishBeanFactoryInitialization()
/**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
// 初始化"conversionService"的bean,此接口用于類型之間的轉(zhuǎn)化溺忧,不是重點盯孙,放過我镀梭,自己去看
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
// 就是為了解析注解的值,沒啥重點
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
return getEnvironment().resolvePlaceholders(strVal);
}
});
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
// 前面說過的埠偿,不是這里的重點
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
// 先初始化 LoadTimeWeaverAware 類型的bean
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
// 看方法名就知道了榜晦,凍結(jié)所有BeanDefinition的元數(shù)據(jù)了
// 沒什么別的目的乾胶,因為到這一步的時候,Spring 已經(jīng)開始預(yù)初始化 singleton beans 了斩郎,
// 肯定不希望這個時候還出現(xiàn) bean 定義解析缩宜、加載甥温、注冊姻蚓。
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// 開始初始化,進來看吧乃秀,重點在里面
beanFactory.preInstantiateSingletons();
}
源碼位置:DefaultListableBeanFactory#preInstantiateSingletons()
@Override
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
// copy 一個包含所有BeanName的集合副本
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 觸發(fā)所有非懶加載的 singleton beans的初始化
for (String beanName : beanNames) {
// Bean 可能有繼承parent的關(guān)系跺讯,獲取合并后的RootBeanDefinition
// 這個知識點用的很少的
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 非抽象殉农、非懶加載的singletons
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// FactoryBean知識點超凳,不了解的看另一篇文章或者自己google
if (isFactoryBean(beanName)) {
// FactoryBean 會在 beanName前面加前綴"&"
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
// SmartFactoryBean, 非重點轮傍,沒深入了解,放過我
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
// 正常的bean都到這里來了杭跪,重點哦涧尿,里面進行初始化了
// 調(diào)用鏈很復(fù)雜姑廉,單獨拉出來講,先繼續(xù)
getBean(beanName);
}
}
}
// 前面流程走完萌踱,說明所有的非懶加載singletonBean 完成了初始化
// Trigger post-initialization callback for all applicable beans...
// 看注釋就懂了号阿,如果我們定義的 bean 是實現(xiàn)了 SmartInitializingSingleton 接口的倦西,
// 那么在這里得到回調(diào)赁严,忽略它吧疼约。
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
小知識點 關(guān)于 lazy-init
ApplicationContext 實現(xiàn)的默認行為就是在啟動時將所有 singleton bean提前進行實例化程剥。提前實例化意味著作為初始化過程的一部分织鲸,ApplicationContext 實例會創(chuàng)建并配置所有的singleton bean。通常情況下這是件好事稳诚,因為這樣在配置中的任何錯誤就會即刻被發(fā)現(xiàn)(否則的話可能要花幾個小時甚至幾天)瀑踢。
有時候這種默認處理可能并不是你想要的橱夭。如果你不想讓一個singleton bean 在ApplicationContext 實現(xiàn)在初始化時被提前實例化棘劣,那么可以將bean設(shè)置為延遲實例化。一個延遲初始化bean 將告訴IoC 容器是在啟動時還是在第一次被用到時實例化舆驶。
需要說明的是,如果一個bean被設(shè)置為延遲初始化拘荡,而另一個非延遲初始化的singleton bean 依賴于它珊皿,那么當ApplicationContext 提前實例化singleton bean時巨税,它必須也確保所有上述singleton 依賴bean也被預(yù)先初始化草添,當然也包括設(shè)置為延遲實例化的bean。因此抄淑,如果Ioc容器在啟動的時候創(chuàng)建了那些設(shè)置為延遲實例化的bean的實例肆资,你也不要覺得奇怪灶芝,因為那些延遲初始化的bean可能在配置的某個地方被注入到了一個非延遲初始化singleton bean里面夜涕。
結(jié)語
以上,本文就是關(guān)于Spring IoC 容器初始化的主要內(nèi)容栖秕。
Spring IoC 的設(shè)計中簇捍,Bean定義的解析和Bean的依賴注入俏拱,是兩個獨立的過程锅必,前面所有內(nèi)容講的就是IoC容器的初始化惕艳,資源定位远搪、載入以及解析BeanDefinition并且注冊谁鳍。
最后一步的實例化所有單例劫瞳,引入了getBean()
方法志于,這就是Spring IoC 依賴注入的入口。也是下節(jié)源碼解讀的主要內(nèi)容养泡。
另外說一句澜掩,上面的源碼解析钥组,肯定不會是完備的程梦,只是提取了我認為重要的東西橘荠。
如有疏漏哥童,敬請諒解和自己查閱相關(guān)資料學習贮懈。如果錯誤,敬請指正各聘!
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布躲因!