上篇文章提到,因爲 Spring 源碼非常龐大,所以閱讀 Spring 源碼的最直接有效的辦法就是 Debug,後續源碼也是基於這種方式去讀。接下來看看一個最簡單的例子
創建一個
UserService
接口和其實現類
public interface UserService {
void save();
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("執行user信息保存操作");
}
}
創建一個
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="userService" class="UserServiceImpl"></bean>
</beans>
編寫一個 JUnite 測試類
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JuniteTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 1.通過byType的方式獲取bean實例
UserService service = context.getBean(UserService.class);
service.save();
System.out.println("-----------");
// 2.通過byName的方式獲取bean實例
UserService service1 = (UserService) context.getBean("userService");
service1.save();
}
}
從 Junite 測試類 創建Context開始,debug 進去可以看到下面這段代碼
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
// 這裏就是真正初始化Spring容器的地方,也就是我在上一篇文章最後提供的那張圖裏的流程
// 有同學會問,那Tomcat啓動的時候是怎麼初始化Spring容器的呢?
// 其實也比較簡單,一般在Tomcat裏面的web.xml文件配置了一個監聽器,大概長這個樣子
//<listener>
// <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
//</listener>
// 該監聽器調用contextInitialized()方法,一路點進去可以看到最後也是調用了AbstractApplicationContext.refresh()方法
refresh();
}
}
找到了 Spring 容器初始化的入口,接下來就來詳細看看我們單測裏面用到的
userService
實例是怎麼被創建出來的
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// Step1:其主要作用就是爲當前的context上下文初始化一些基礎參數
// 比如說:初始化時間,活動狀態,日誌信息等等,暫時不重要,不需要關注其太多的實現細節
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// Step2: 從註釋上看是通知子類去刷新內部bean工廠。解釋的好像也特別抽象不知道它到底想幹啥
// **重要** 其實在這個方法裏面做了幾個非常重要的事情,創建bean工廠,並將BeanDefinitions對象(bean實例的定義,這個對象也會貫穿整個Spring源碼)存到bean工廠中
// 創建出來的ConfigurableListableBeanFactory,這個bean工廠會貫穿整個Spring源碼
// 它功能是存放beanDefinition定義,後續創建出來的bean實例也存放這這個工廠裏面
// 暫時先理解到這裏就可以了,後續我們會展開其裏面的具體實現
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// Step3:配置bean工廠的上下文,
// 比如在這個裏面配置了ClassLoader,
// 配置了BeanPostProcessor(bean後置處理器,這玩意兒會在bean實例初始化前後做一些增強工作,後面會詳細介紹)
// 註冊了三個默認bean實例,分別是 “environment”、“systemProperties”、“systemEnvironment” 瞭解即可
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// Step4:本方法沒有具體實現,是一個擴展點,開發人員可以根據自己的情況做具體的實現
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// Step5:bean工廠後置處理器,主要是對beanFactory對象做一些後置操作
// 和registerBeanPostProcessor名字有點像,只不過後者操作的對象bean實例
// 後續會深入瞭解其實現原理
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// Step6:註冊所有實現了BeanPostProcessor接口的對象,
// 注意:這裏只是註冊實例,並沒有執行其方法,方法具體調用時機是在bean實例初始化前後執行的
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// Step7:初始化message資源,做一些國際化相關操作,瞭解即可
initMessageSource();
// Initialize event multicaster for this context.
// Step8:初始化事件廣播器,用於發送相關事件
// 比如註冊了一個bean實例的時候就會發送一個事件
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// Step9:在一些特定的context中初始化一些特殊的bean,沒有具體實現,留給開發者自定義一些操作
onRefresh();
// Check for listener beans and register them.
// Step10:註冊監聽器,瞭解即可
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// Step11:初始化所有非懶加載的bean實例,注意是非懶加載的bean
//**非常重要** 我們代碼中需要用到的bean實例幾乎都是從這裏創建出來的,後面會展開其具體實現
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// Step12:完成上線文刷新,發佈上下文初始化完畢的事件
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.
// 當創建bean發生異常時,先銷燬已經創建出來的單例bean實例,從而避免資源一直被佔用。
// 這樣的做法有點像關閉資源鏈接,比如當程序聯繫MySQL或Redis的時候,程序發生異常時需要將鏈接斷開一樣
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...
// Step13:重置公共的一些緩存數據
resetCommonCaches();
}
}
}
總結
Spring 容器初始化,共經歷了 13 步;其中尤其需要重點關注的是:
Step2,初始化 Spring 容器,並構建了
BeanDefinition
定義Step5,
BeanFactoryPostProcessor
,對BeanFactory
做一些後置操作Step7,
BeanPostProcessor
,對 bean 實例在初始化前後做一些增強工作Step11,對剩餘所有的非懶加載的BeanDefinition
(bean 定義)執行 bean 實例化操作
下一篇文章我們來講講 Step2,obtainFreshBeanFactory()
實現原理。
歡迎關注我,共同學習