Spring源碼系列整體欄目
內(nèi)容 | 鏈接地址 |
---|---|
【一】spring源碼整體概述 | https://blog.csdn.net/zhenghuishengq/article/details/130940885 |
【二】通過refresh方法剖析IOC的整體流程 | https://blog.csdn.net/zhenghuishengq/article/details/131003428 |
【三】xml配置文件啟動(dòng)spring時(shí)refresh的前置工作 | https://blog.csdn.net/zhenghuishengq/article/details/131066637 |
【四】注解方式啟動(dòng)spring時(shí)refresh的前置工作 | https://blog.csdn.net/zhenghuishengq/article/details/131113249 |
【五】refresh中prepareRefresh的執(zhí)行流程 | https://blog.csdn.net/zhenghuishengq/article/details/131186016 |
一,深度剖析refresh的prepareRefresh方法
前兩篇談到了refresh方法的前置工作和準(zhǔn)備工作有哪些,注解的方式相對而言會(huì)比xml的方式需要做的前置工作更多。接下來就是進(jìn)入最主要的refresh部分,前面幾篇也粗略的對refresh里面的12個(gè)方法進(jìn)行了粗略的概括,接下來的文章中,將對每一個(gè)方法做一個(gè)詳細(xì)的概括。
再次查看這個(gè)refresh方法,首先會(huì)在這個(gè)方法上面加一個(gè)同步鎖 synchronized ,用來保證線程的安全性
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1:準(zhǔn)備刷新上下文環(huán)境
prepareRefresh();
//2:獲取告訴子類初始化Bean工廠,不同工廠有不同的實(shí)現(xiàn)
// 并且將配置文件的屬性值加載到當(dāng)前工廠中
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//3:對bean工廠進(jìn)行填充屬性
prepareBeanFactory(beanFactory);
try {
// 第四:留個(gè)子類去實(shí)現(xiàn)該接口,bean工廠的后置處理器
postProcessBeanFactory(beanFactory);
// 調(diào)用我們的bean工廠的后置處理器.
//1. 會(huì)在此將class掃描成beanDefinition 2.bean工廠的后置處理器調(diào)用
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊我們bean的后置處理器
registerBeanPostProcessors(beanFactory);
// 初始化國際化資源處理器.
initMessageSource();
// 創(chuàng)建事件多播器
initApplicationEventMulticaster();
// 這個(gè)方法同樣也是留個(gè)子類實(shí)現(xiàn)的springboot也是從這個(gè)方法進(jìn)行啟動(dòng)tomcat的.
onRefresh();
//把我們的事件監(jiān)聽器注冊到多播器上
registerListeners();
// 實(shí)例化我們剩余的單實(shí)例bean.
finishBeanFactoryInitialization(beanFactory);
// 最后容器刷新 發(fā)布刷新事件(Spring cloud也是從這里啟動(dòng)的)
finishRefresh();
}
}
}
1,prepareRefresh()具體的執(zhí)行流程
接下來就是refresh中的第一個(gè)方法,也是本文的重點(diǎn):prepareRefresh(),顧名思義,就是刷新前的準(zhǔn)備工作,接下來進(jìn)入這個(gè)方法中,查看內(nèi)部詳細(xì)的執(zhí)行流程
其主要代碼片段如下
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
/**
* 比如我們自己寫一個(gè)類重寫了initPropertySources方法,在該方法中設(shè)置了一個(gè)環(huán)境變量的值為A
* 啟動(dòng)的時(shí)候,我的環(huán)境變量中沒有該值就會(huì)啟動(dòng)拋出異常
*/
initPropertySources();
/**
* 用來校驗(yàn)我們?nèi)萜鲉?dòng)必須依賴的環(huán)境變量的值
*/
getEnvironment().validateRequiredProperties();
/**
* 創(chuàng)建一個(gè)早期事件監(jiān)聽器對象
*/
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet < > (this.applicationListeners);
} else {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
this.earlyApplicationEvents = new LinkedHashSet < > ();
}
1,首先第一個(gè)是記錄一下運(yùn)行這個(gè)方法時(shí)的起始時(shí)間,最后通過方法結(jié)束獲取的時(shí)間,來減去這個(gè)時(shí)間,就可以得到整個(gè)運(yùn)行refresh方法的差值
this.startupDate = System.currentTimeMillis(); //容器啟動(dòng)的時(shí)間
2,隨后就是設(shè)置兩個(gè)標(biāo)志位,一個(gè)是設(shè)置容器關(guān)閉的標(biāo)志位,一個(gè)是設(shè)置容器激活的標(biāo)志位
this.closed.set(false); //容器關(guān)閉的標(biāo)志位
this.active.set(true); //容器激活的標(biāo)志位
3,隨后就是一個(gè)重要的方法 initPropertySources() ,該方法是一個(gè)空方法,主要是留給子類自己去實(shí)現(xiàn),下面會(huì)專門舉一個(gè)小demo來理解這個(gè)方法的作用。
protected void initPropertySources() {
// For subclasses: do nothing by default.
}
4,隨后一步就是先獲取系統(tǒng)環(huán)境對象,在進(jìn)入refresh方法之前就已經(jīng)獲取的 StandardEnvironment 實(shí)例對象,因此這里的環(huán)境對象不為空,直接將對象值返回。該對象內(nèi)部包含了全部的系統(tǒng)變量和系統(tǒng)屬性。
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
在拿到這個(gè)標(biāo)準(zhǔn)的環(huán)境對象之后,接下來就是通過這個(gè)validateRequiredProperties對里面的屬性進(jìn)行一個(gè)驗(yàn)證的操作
getEnvironment().validateRequiredProperties();
其對應(yīng)的驗(yàn)證操作如下,會(huì)循環(huán)遍歷這些屬性,判斷屬性在系統(tǒng)變量中是否存在,如果不存在則拋出異常
@Override
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key: this.requiredProperties) {
//判斷當(dāng)前屬性是否存在set集合中,即是否為系統(tǒng)屬性
if (this.getProperty(key) == null) {
//如果當(dāng)前屬性不是系統(tǒng)屬性,則添加一個(gè)異常
ex.addMissingRequiredProperty(key);
}
}
//如果異常數(shù)量不為空,則拋出異常
if (!ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
在這個(gè)拋出的異常中,就打印了這么一句話
The following properties were declared as required but could not be resolved:
5,隨后一步就是創(chuàng)建一個(gè)事件監(jiān)聽對象,在spring啟動(dòng)的時(shí)候,這個(gè)早期的earlyApplicationListeners 監(jiān)聽器對象默認(rèn)為空,但是在springboot中,由于在spring.factories中存在大量的事件需要先加載,因此在springBoot中該對象不為空,相當(dāng)于是在spring的基礎(chǔ)上的一個(gè)增強(qiáng)。所以需要對這個(gè)earlyApplicationListeners對象判斷是都為空。
if (this.earlyApplicationListeners == null) {
//spring使用,默認(rèn)為空
this.earlyApplicationListeners = new LinkedHashSet <> (this.applicationListeners);
} else {
// Reset local application listeners to pre-refresh state.
//springboot使用,先清除原有的,再添加
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
如在spring.factories的文件中,默認(rèn)就會(huì)注入這么多的監(jiān)聽器。該功能主要是springboot的一個(gè)擴(kuò)展機(jī)制
6,除了這個(gè)監(jiān)聽器在spring啟動(dòng)時(shí)為空之外,這個(gè)早期Event事件也為空。
this.earlyApplicationEvents = new LinkedHashSet<>();
2,initPropertySources(可擴(kuò)展)
由于initPropertySources 這個(gè)方法內(nèi)部是一個(gè)空方法,主要是留給子類去實(shí)現(xiàn),因此在這里,可以定義一個(gè)類繼承 ClassPathXmlApplicationContext 這個(gè)類,如下
/**
* @author zhenghuisheng
* @date : 2023/6/6
*/
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
//String... locations:xml配置文件的路徑
public MyClassPathXmlApplicationContext(String... locations) {
super(locations);
}
@Override
protected void initPropertySources() { / /初始化屬性
//設(shè)置屬性
getEnvironment().setRequiredProperties("USERNAME");
//獲取屬性
String requiredProperty = getEnvironment().getRequiredProperty("USERNAME");
System.out.println("當(dāng)前系統(tǒng)用戶名稱為:" + requiredProperty);
//獲取app名稱
String applicationName = getApplicationName();
System.out.println("當(dāng)前應(yīng)用名稱為:" + applicationName);
//獲取前置處理器
List<BeanFactoryPostProcessor> list = getBeanFactoryPostProcessors();
System.out.println("定義的bean工廠的后置處理器為的集合長度為:"+ list.size());
...
}
}
在上一篇中有寫到,在refresh方法之前的前置工作中,就會(huì)獲取所有的系統(tǒng)環(huán)境和系統(tǒng)變量,如下圖就是系統(tǒng)環(huán)境變量
因此這里可以直接拿到這些環(huán)境變量等。隨后寫一個(gè)主啟動(dòng)類進(jìn)行測試
public static void main(String[] args) {
//擴(kuò)展,用于鑒定屬性
ApplicationContext m = new MyClassPathXmlApplicationContext("classpath.a.xml");
}
由于環(huán)境對象可以獲取到,因此在這個(gè)環(huán)境變量里面設(shè)置一個(gè)屬性
getEnvironment().setRequiredProperties("USERNAME");
上面設(shè)置的屬性 USERNAME值,在設(shè)置之后會(huì)進(jìn)行一個(gè)驗(yàn)證的操作,主要是驗(yàn)證該值在系統(tǒng)中是否存在,如果不存在則直接拋出異常。
void validateRequiredProperties() throws MissingRequiredPropertiesException;
開發(fā)中如果程序是需要包含某個(gè)值, 如需要某個(gè)屬性或者某個(gè)前綴之類的,就可以在這一步進(jìn)行判斷,就不用進(jìn)應(yīng)用層里面去判斷了。如在抽象類 AbstractEnvironment 中,有著這兩個(gè)的具體實(shí)現(xiàn),可以先設(shè)置值,隨后會(huì)對設(shè)置的值進(jìn)行驗(yàn)證。
//設(shè)置相關(guān)的屬性值
@Override
public void setRequiredProperties(String...requiredProperties) {
this.propertyResolver.setRequiredProperties(requiredProperties);
}
//驗(yàn)證屬性值,屬性在在系統(tǒng)屬性中不存在,則立馬拋出異常
@Override
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
this.propertyResolver.validateRequiredProperties();
}
驗(yàn)證的流程具體如下:判斷當(dāng)前系統(tǒng)屬性有沒有這個(gè)值,如果系統(tǒng)有這個(gè)值,則不管;沒有這個(gè)值,則拋出異常報(bào)錯(cuò)
@Override
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key: this.requiredProperties) {
//判斷當(dāng)前系統(tǒng)屬性有沒有這個(gè)值
if (this.getProperty(key) == null) {
ex.addMissingRequiredProperty(key);
}
}
//如果系統(tǒng)有這個(gè)值,則不管;沒有這個(gè)值,則拋出異常報(bào)錯(cuò)
if (!ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
主要作用就是提前做一個(gè)參數(shù)的校驗(yàn),實(shí)際開發(fā)中用的也比較少。文章來源:http://www.zghlxwxcb.cn/news/detail-481251.html
3,總結(jié)
在這個(gè)prepareRefresh方法中,總結(jié)來說就是做了五件事情:設(shè)置容器啟動(dòng)時(shí)間、設(shè)置活躍狀態(tài)為true、設(shè)置關(guān)閉狀態(tài)為false、獲取環(huán)境對象,并將當(dāng)前系統(tǒng)的屬性值設(shè)置到Environment對象中、實(shí)例化監(jiān)聽器對象和事件對象,并設(shè)置為空。文章來源地址http://www.zghlxwxcb.cn/news/detail-481251.html
到了這里,關(guān)于【spring源碼系列-05】refresh中prepareRefresh方法的執(zhí)行流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!