Spring 工程執(zhí)行過程
1. 前言
Spring 框架是如何工作的?
本節(jié)目的在于幫助大家理解 Spring 框架底層干了什么事情。
在上一節(jié)中我們通過一個入門工程簡單地體驗了一把 Spring 的使用。
我們發(fā)現(xiàn),通過構造一個 ClassPathXmlApplicationContext
對象,加載項目的 applicationContext.xml
文件,確實可以實例化對象。
疑問導出
而腦海中不禁有一個想法… Spring 如何初始化對象的實例的?我們又如何從容器中獲取得到對象的實例的呢?
帶著疑問… 開啟本節(jié)的源碼和原理之旅。
2. 容器初始化
回顧代碼:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService service = (UserService) context.getBean("userService");
service.saveUser();
}
在上面的代碼中可以得知 Spring 的容器是 ApplicationContext
,那么它到底是什么東西呢?先跟我一起追蹤一下它的角色。
官方文檔
慕課解釋
簡單翻譯過來就是 ApplicationContext
是一個 接口,是 BeanFactory
這個接口的子接口,它擴展了 BeanFactory
這個接口,提供了額外附加的功能。
而 BeanFactory
是管理 bean 對象的容器的根接口,大家了解下就好,我們是針對它的子接口 ClassPathXmlApplicationContext
做的實例化,目的是加載項目中的 Spring 的配置文件,使 Spring 來管理我們定義的 bean 對象。
疑問導出
那么我們的問題是…ClassPathXmlApplicationContext
對象實例化之后,干了哪些事情呢?
2.1 容器初始化執(zhí)行動作
applicationContext
實例化執(zhí)行代碼邏輯 。
我們追蹤下源碼,發(fā)現(xiàn) ClassPathXmlApplicationContext
初始化的時候,它做了一系列的事情。源碼如下:
代碼解釋:
- 是初始化
ClassPathXmlApplicationContext
對象執(zhí)行的有參構造; - 加載項目下的 xml 配置文件;
- 調用 refresh 刷新容器的方法 bean 的實例化就在這個方法中。
繼續(xù)跟蹤:
2.2 容器初始化 bean 對象動作
下面是從源碼中粘貼的部分代碼
步驟闡述:
對于我們而言 這些英文看起來很吃力… 放輕松大家,我們只關注對我們理解流程有用的代碼:
- 1 的位置:是準備刷新,那么 Spring 只是設置刷新的標記,加載了外部的
properties
屬性文件; - 2 的位置:是準備 bean 工廠對象;
- 3 的位置:這一步驟就加載了配置文件中的所有 bean 標簽,但是并沒有對他們進行實例化;
- 4 的位置:完成此上下文的 bean 工廠的初始化,初始化所有剩余的單例 bean。(Spring 中默認加載的 bean 就是單例模式后面生命周期會講)
- 最后的位置:完成容器的刷新,也就是所有的 bean 初始化完成。
//這里粘貼一部分初始化代碼的邏輯 幫助大家理解
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
// Trigger initialization of all non-lazy singleton beans...
//所有非懶加載的單例bean的觸發(fā)器初始化。。。
for (String beanName : beanNames) {
...//省略循環(huán)的代碼
}
OK 上面就是加載配置文件后 Spring 框架做的所有事情,當然實際底層涉及的東西 更多,但是我們沒有必要深究,畢竟我們是理解過程,不是追求實現(xiàn)。
疑問導出:
我們整理了 Spring 初始化 bean 對象的過程,那么如果容器中確實存在了 bean 的實例,我們是如何獲取得到的呢?
3. 容器中獲取對象的過程
還是先看下我們獲取容器對象的代碼:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService service = (UserService) context.getBean("userService");
service.saveUser();
}
代碼分析:
context.getBean
的方法是通過 bean 標簽里的 id 來從容器中獲取,那么我們看下源碼 :
在父類 AbstractApplicationContext
中有對 getBean 方法的實現(xiàn)。
@Override
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
追蹤父類方法
最終通過我們層層追蹤,我們在 AbstractAutowireCapableBeanFactory
中發(fā)現(xiàn)這樣的一段代碼:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//...
//省略大量方法內部代碼
//...
// Initialize the bean instance.
Object exposedObject = bean;
try {
//給實例中的屬性賦值
populateBean(beanName, mbd, instanceWrapper);
//真實實例化對象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//...
//繼續(xù)省略大量方法
//...
// Register bean as disposable.
try {
//將實例化后的對象放入容器中
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
//返回實例化后的對象實例
return exposedObject;
}
上面源碼中我們可以看到: 對象實例的獲取好像是在獲取的時候執(zhí)行的 doCreateBean
,那么之前記載的 xml
文件不是實例過了嗎?稍微解釋下:加載文件時候的實例化操作,其實是實例化了一個 Spring 框架提供的對象,作用是對于我們 bean 對象做描述,這里才是真實的實例化動作。我們再看看 registerDisposableBeanIfNecessary
這個方法做的是什么。
public void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
結論
一切真相大白。它其實就是一個 map 集合 ,這個 map 集合的 key 就是我們定義的 bean 的 id 或者 bean 的 name ,那么值就是對象的實例。
4. 小結
本章節(jié) 帶著大家梳理了一下 Spring 初始化 bean 和獲取 bean 的流程:
- Spring 框架通過 ResourceLoader 加載項目的 xml 配置文件;
- 讀取 xml 的配置信息 變成對象存儲,但未實例化;
- 通過 bean 工廠處理器對 bean 做實例化,存儲到一個 map 集合中默認是單例;
- 獲取對象 通過 xml 文件中 bean 的 id 從 map 集合中通過 get (key) 獲取。
羅馬不是一天建成的 ,書山有路勤為徑…