Spring 對於Java 開發來講,以及算得上很是基礎而且核心的框架了,在有必定開發經驗後,閱讀源碼能更好的提升咱們的編碼能力而且讓咱們對其更加理解。俗話說知己知彼,百戰不殆。當你對Spring 掌握熟透以後,也就沒什麼能過阻攔你在開發路上前進了。html
IOC 整體來講有兩處地方最重要,一個是建立Bean容器,一個是初始化。在本文中,主要爲你們講解了 IOC Bean 容器建立過程。後續將會補上初始化部分的知識。java
爲了保持文章的嚴謹性,若是讀者發現我哪裏說錯了請必定不吝指出,很是但願能夠聽到讀者的聲音。同時能過糾正本身的誤解。spring
本文主要採用了ClassPathXmlApplication做爲Spring IOC 容器,從 ClassPathXmlApplication 分析容器建立的過程,首先來看一下ClassPathXmlApplication 的依賴關係: 數組
首先咱們先來觀察一下核心主幹:bash
咱們能夠經過對ClassPathXmlApplicationContext 的父類名稱,瞭解其主要功能:app
經過上述主要類的分析,相信你們對Spring IOC 的初始化有了大概的認識。框架
簡單來講,Spring IOC 容器建立的具體流程以下:ide
咱們將圖片對應到類的調用,具體步驟爲:源碼分析
一、ClassPathXmlApplicationContext -> 經過構造器,讀取XML配置文件地址信息ui
二、AbstractApplicationContext -> refresh() 初始化容器
三、AbstractRefreshableApplicationContext -> BeanFactory 初始化
四、AbstractXmlApplicationContext -> loadBeanDifinition 讀取資源信息(加載Bean)
五、XmlBeanDefinitionReader -> 將資源文件解析成 BeanDefinition
六、DefaultBeanDefinitionDocumentReader -> 將BeanDefinition 註冊到BeanFactory 中
因爲Spring IOC部分源碼主要包括了:容器建立以及Bean 初始化兩部分。在本文內容中,主要介紹一下,容器建立的過程!
ApplicationContext.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="User" class="com.charles.business.model.User">
<property name="username" value="jaycekon"/>
<property name="phone" value="1881412***"/>
</bean>
</beans>
複製代碼
代碼啓動入口:
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
User user = (User) applicationContext.getBean("User");
Assert.notNull(user, "容器初始化異常!");
logger.info("初始化結果:{}", JSONObject.toJSONString(user));
}
複製代碼
接下來咱們根據ClassPathXmlApplication 的依賴圖進行逐一分析每個過程。
核心方法:
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
複製代碼
從上圖能夠看出,ClassPathXmlApplicationContext 類中並無提供什麼特別的方法,除構造器外,只有一個獲取Resource 的方法(與之類似的 AbstractRefreshableConfigApplicationContext 中提供類獲取資源定位的方法),主要用於保存資源信息。
ClassPathXmlApplicationContext 中的主要構造方法:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 調用父類構造方法
super(parent);
// AbstractRefreshableConfigApplicationContext 具體實現,保存資源定位信息
setConfigLocations(configLocations);
if (refresh) {
// AbstractApplicationContext 具體實現,初始化容器與Bean
refresh();
}
}
複製代碼
能夠看到,ClassPathXmlApplicationContext 這個類至關簡單,主要做用:
核心方法:
org.springframework.context.support.AbstractApplicationContext#refresh
複製代碼
AbstractApplicationContext 是Spring IOC 容器中核心類,對父類,接口提供了具體實現,其中的方法很是豐富,在這裏咱們主要介紹其中的核心方法 refresh()。 這裏簡單說下爲何是 refresh(),而不是 init() 這種名字的方法。由於 ApplicationContext 創建起來之後,其實咱們是能夠經過調用 refresh() 這個方法重建的,這樣會將原來的 ApplicationContext 銷燬,而後再從新執行一次初始化操做。
public void refresh() throws BeansException, IllegalStateException {
//對容器初始化進行加鎖操做,比免在建立的同時,重複操做。
synchronized (this.startupShutdownMonitor) {
// 設置容器初始化世界,表示容器如今isActive,初始化上下文環境中的任何佔位符屬性源
prepareRefresh();
// 核心步驟,主要功能是:讓子類進行BeanFactory 初始化,而且將Bean信息 轉換爲BeanFinition,最後註冊到容器中
// 固然,這裏說的 Bean 尚未初始化,只是配置信息都提取出來了
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
prepareBeanFactory(beanFactory);
try {
// 後續步驟將在下次進行分析,本文主要核心爲上面幾個步驟
...
}
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(); } } } 複製代碼
從AbstractApplicationContext 中的refresh()咱們能夠粗略的領會到,Spring IOC 的核心流程,基本在這裏完成的,從容器建立,資源解析,Bean建立等一系列步驟。都是由這個方法進行控制。
在本文中,咱們主要關心的一個點就是:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
複製代碼
經過上圖,咱們能夠直觀的瞭解到ApplicationContext 與BeanFactory 之間的關係。這裏建立的 ConfigurableListableBeanFactory 實際包含了BeanFactory 三個分支的絕大部分功能。
一、ApplicationContext 繼承了 ListableBeanFactory,這個 Listable 的意思就是,經過這個接口,咱們能夠獲取多個 Bean,最頂層 BeanFactory 接口的方法都是獲取單個 Bean 的。
二、ApplicationContext 繼承了 HierarchicalBeanFactory,Hierarchical 單詞自己已經能說明問題了,也就是說咱們能夠在應用中起多個 BeanFactory,而後能夠將各個 BeanFactory 設置爲父子關係。
三、AutowireCapableBeanFactory 這個名字中的 Autowire 你們都很是熟悉,它就是用來自動裝配 Bean 用的,可是仔細看上圖,ApplicationContext 並無繼承它,不過不用擔憂,不使用繼承,不表明不可使用組合,若是你看到 ApplicationContext 接口定義中的最後一個方法 getAutowireCapableBeanFactory() 就知道了。
四、ConfigurableListableBeanFactory 也是一個特殊的接口,看圖,特殊之處在於它繼承了第二層全部的三個接口,而 ApplicationContext 沒有。這點以後會用到。
咱們來看一下 obtainFreshBeanFactory() 方法的主要內容:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//AbstractRefreshableApplicationContext 中實現,主要用於刷新BeanFactory,加載 Bean 定義、註冊 Bean 等等
refreshBeanFactory();
//獲取上述步驟中 建立好的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
複製代碼
核心方法:
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
複製代碼
其實在閱讀源碼的過程當中,讀懂類的命名能過爲咱們提供很大的幫助,例如 AbstractRefreshableApplicationContext 這個類,能夠看出她繼承了AbstractApplicationContext 而且主要實現了其中的refresh 功能,固然,這裏的refresh 並非指 refresh() 方法。而是指的:refreshBeanFactory()
@Override
protected final void refreshBeanFactory() throws BeansException {
//判斷當前ApplicationContext是否已經有 BeanFactory ,若是有,銷燬全部 Bean,關閉 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化一個 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 設置 BeanFactory 的兩個配置屬性:是否容許 Bean 覆蓋、是否容許循環引用
customizeBeanFactory(beanFactory);
// 讀取配置文件信息,加載 Bean 到 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);
}
}
複製代碼
可能你們以前會以前會認爲ApplicationContext 與 BeanFactory 之間是繼承關係,可是在實際應用中,可是它不該該被理解爲 BeanFactory 的實現類,ApplicationContext 是持有 BeanFactory 的一個實例,而且之後全部的 BeanFactory相關的操做實際上是給這個實例來處理的。
簡單介紹一下 customizeBeanFactory 方法:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
複製代碼
核心方法:
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
複製代碼
經歷了那麼長的路程,才終於到了XML -> Resource -> BeanDefinition 這個步驟:
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//給這個 BeanFactory 實例化一個 XmlBeanDefinitionReader BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 設置默認環境配置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 BeanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
// 將配置文件讀取,解析成BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
複製代碼
這個方法比較簡單,咱們直接往下走,進入到loadBeanDefinitions()方法中:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
複製代碼
能夠看到,上面主要分紅了兩個步驟,一個是經過Resource 進行解析,一個是經過configLocations 進行解析(這個步驟,主要多了將資源定位符抓換爲Resource 的過程)。
後續的Bean 解析,將會在下述方法進行解析:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
複製代碼
因爲篇幅緣由,本文主要介紹了Spring IOC 容器建立的源碼過程,關於Bean 解析,以及後續步驟的源碼分析,將會在後續補上。最後咱們來回顧一下主流程:
參考資料: