1. 程式人生 > 其它 >Spring IOC原始碼(二):IOC容器之 重新整理前的準備

Spring IOC原始碼(二):IOC容器之 重新整理前的準備

1、原始碼解析

  prepareRefresh()容器重新整理refresh()的第一個方法,是容器重新整理前的準備工作。
 1 // 容器啟動的開始時間  毫秒級
 2 private long startupDate;
 3 // 容器目前是否活躍的標記
 4 private final AtomicBoolean active = new AtomicBoolean();
 5 // 當前容器是否已經被關閉的標記
 6 private final AtomicBoolean closed = new AtomicBoolean();
 7 // 容器重新整理前註冊進來的監聽器集合
 8 private Set<ApplicationListener<?>> earlyApplicationListeners;
 9 // 容器的監聽器集合
10 private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
11 // 要釋出的應用程式事件集合(在多播器未設定之前要釋出的事件)
12 private Set<ApplicationEvent> earlyApplicationEvents;
13 
14 // 重新整理容器前的準備工作
15 protected void prepareRefresh() {
16    // Switch to active.
17    // 記錄容器啟動時間
18    this.startupDate = System.currentTimeMillis();
19    // 設定關閉狀態為false
20    this.closed.set(false);
21    // 設定活躍狀態為true
22    this.active.set(true);
23 
24    // 日誌記錄
25    if (logger.isDebugEnabled()) {
26       if (logger.isTraceEnabled()) {
27          logger.trace("Refreshing " + this);
28       }
29       else {
30          logger.debug("Refreshing " + getDisplayName());
31       }
32    }
33 
34    // 具體實現由子類完成,初始化屬性資源
35    initPropertySources();
36 
37    // 獲取Environment物件,並驗證當前系統需要的屬性值是否都載入到了Environment物件中
38    getEnvironment().validateRequiredProperties();
39 
40    // 準備監聽器和事件的集合物件,預設為空的集合
41    if (this.earlyApplicationListeners == null) {
42       this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
43    }
44    else {
45       // 重置本地應用監聽器為預重新整理狀態,清空集合快取
46       this.applicationListeners.clear();
47       // 將容器重新整理前的監聽器與事件集合物件設定到當前容器監聽器集合中
48       this.applicationListeners.addAll(this.earlyApplicationListeners);
49    }
50    
51    // 建立重新整理前的事件集合,依賴於多播器,一旦多播器是可用的,事件集合中的事件就可以被髮布
52    this.earlyApplicationEvents = new LinkedHashSet<>();
53 }

綜上可見,容器重新整理前的準備工作prepareRefresh()完成以下事情:

  1、記錄容器啟動時間

  2、設定當前容器關閉狀態為false   3、設定當前容器活躍狀態為true   4、設定拓展點,初始化資源佔位符   5、設定環境Environment物件,並將當前系統屬性值載入至環境物件中   6、監聽器、事件集合的處理 - Spring框架提供的擴充套件點,在SpringBoot中有應用。

2、應用

  Spring在prepareRefresh()有對初始化資源佔位符initPropertySources()的拓展點,下面我們來看看如何實現拓展。   initPropertySources()方法是AbstractApplicationContext中的待子類實現的方法。
protected void initPropertySources() {
   // For subclasses: do nothing by default.
}

  從 IOC原始碼(一):IOC容器啟動流程核心方法概覽中ClassPatchXmlApplicationContext類圖中我們得知,ClassPatchXmlApplicationContext是AbstractApplicationContext的子類,下面我們來看看初始化資源佔位符的具體實現。

2.1、定義配置檔案 - snailsInitPropertySource.xml

1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3       xmlns:context="http://www.springframework.org/schema/context"
4       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
6        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">
7 
8 </beans>

2.2、拓展初始化屬性資源,重寫initPropertySources()方法

 1 import org.springframework.beans.BeansException;
 2 import org.springframework.context.support.ClassPathXmlApplicationContext;
 3 
 4 // 初始化屬性資源的拓展實現
 5 public class SnailsInitApplicationContext extends ClassPathXmlApplicationContext {
 6   
 7    public SnailsInitApplicationContext(String... configLocations) throws BeansException {
 8       super(configLocations);
 9    }
10 
11    @Override
12    protected void initPropertySources() {
13       System.out.println("expand initPropertySource... ");
14       // 新增屬性到Environment環境中
15       getEnvironment().getSystemProperties().put("address","中國");
16       // Environment環境中的必輸屬性,會通過validateRequiredProperties方法做校驗
17        getEnvironment().setRequiredProperties("addr");
18    }
19 }

 2.3、測試程式碼

 1 /**
 2  * @Description: 測試拓展初始化資源屬性拓展
 3  * @author: snails
 4  * @since: 2022/3/1 11:02
 5  */
 6 public class TestSnailsInitApplicationContext {
 7    public static void main(String[] args) {
 8       SnailsInitApplicationContext context = new SnailsInitApplicationContext("snailsInitPropertySource.xml");
 9    }
10 }

2.4、執行結果

  getEnvironment().getSystemProperties().put("address","中國");   address屬性已經載入到environment#propertySources#propertySourceList集合中name為systemProperties的 source集合中,source中儲存的是Properties物件。   getEnvironment().setRequiredProperties("addr");
Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [addr]
at org.springframework.core.env.AbstractPropertyResolver.validateRequiredProperties(AbstractPropertyResolver.java:145)
at org.springframework.core.env.AbstractEnvironment.validateRequiredProperties(AbstractEnvironment.java:519)
at org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:646)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:153)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:97)
at selfInitPropertySources.SnailsInitApplicationContext.<init>(SnailsInitApplicationContext.java:14)
at selfInitPropertySources.TestSnailsInitApplicationContext.main(TestSnailsInitApplicationContext.java:11)

校驗失敗,拋異常,校驗流程分析如下

  1、將要校驗的屬性addr加入校驗集合requiredProperties中;   2、AbstractPropertyResolver#validateRequiredProperties()校驗,遍歷校驗集合requiredProperties,通過key=addr從environment中的source集合中獲取value;   3、若通過key獲取的value為空,丟擲異常。