1. 程式人生 > >spring4.1.8初始化原始碼學習三部曲之二:setConfigLocations方法

spring4.1.8初始化原始碼學習三部曲之二:setConfigLocations方法

本章是學習spring4.1.8初始化原始碼的第二篇,前一章《spring4.1.8初始化原始碼學習三部曲之一:AbstractApplicationContext構造方法》對AbstractApplicationContext的初始化做了分析,本章我們聚焦ClassPathXmlApplicationContext.setConfigLocations方法; 整體概括

本章會涉及到多個類的細節,所以先從整體上概括AbstractRefreshableConfigApplicationContext.setConfigLocations方法的主要功能,後面的細節都是基於這些總結展開的: 1. setConfigLocations主要工作有兩個:建立環境物件ConfigurableEnvironment 、處理ClassPathXmlApplicationContext傳入的字串中的佔位符; 2. 環境物件ConfigurableEnvironment中包含了當前JVM的profile配置資訊、環境變數、 Java程序變數; 3. 處理佔位符的關鍵是ConfigurableEnvironment、PropertyResolver、PropertyPlaceholderHelper之間的配合: 名稱 作用 ConfigurableEnvironment 1.建立PropertyResolver; 2.向PropertyResolver提供環境變數、 Java程序變數 PropertyResolver 1.建立PropertyPlaceholderHelper; 2.定義佔位符的字首和字尾(placeholderPrefix、placeholderSuffix); 3.提供getPropertyAsRawString方法給PropertyPlaceholderHelper呼叫,用來獲取指定key對應的環境變數; PropertyPlaceholderHelper 1.找到字串中的佔位符; 2.呼叫PropertyResolver.getPropertyAsRawString方法,從環境變數中取出佔位符對應的值 3.用環境變數的值替換佔位符;

用思維導圖來輔助: 這裡寫圖片描述

對佔位符的處理實戰,請參考文章《windows下修改、編譯、構建spring-framework4.1.8.RELEASE原始碼》,這裡面用demo展示了佔位符的替換; 展開詳情

接下來去閱讀setConfigLocations方法內部的細節程式碼:

跟蹤該方法,找到是在類AbstractRefreshableConfigApplicationContext中實現的:

public void setConfigLocations(String… locations) { if (locations != null) { Assert.noNullElements(locations, “Config locations must not be null”); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }

  1. 從上述程式碼可以發現,本章我們要重點學習的是resolvePath(locations[i]),結合上一章demo中的入參,此處應該是方法resolvePath(“classpath:applicationContext.xml”);
  2. 跟蹤到AbstractRefreshableConfigApplicationContext類,這個方法的目的是替換掉path字串中的佔位符${XXX}這樣的內容:

protected String resolvePath(String path) { return getEnironment().resolveRequiredPlaceholders(path); }

  1. 先看getEnvironment()方法:

@Override public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment; }

  1. 關於ConfigurableEnvironment介面,我們先來看看他的宣告方法以及繼承關係: 這裡寫圖片描述 從上圖可見,ConfigurableEnvironment介面有兩個重要部分組成:Profile和Property; Profile是對測試、生產等不同環境下的bean配置,這裡我們沒有特別設定,所以用到的profile是AbstractEnvironment的defaultProfiles; 接下來關於Property資源是如何產生的;

  2. 順著呼叫一路看過去,發現最終會呼叫AbstractEnvironment類的構造方法:

public AbstractEnvironment() { customizePropertySources(this.propertySources); if (this.logger.isDebugEnabled()) { this.logger.debug(format( “Initialized %s with PropertySources %s”, getClass().getSimpleName(), this.propertySources)); } }

  1. 上面的customizePropertySources是在StandardEnvironment類中實現的,如下:

@Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); }

上述程式碼中,可以將propertySources物件理解成一個容器(其物件內部核心成員propertySourceList是個CopyOnWriteArrayList例項); 首先向propertySources新增一組屬性,來自Java程序變數(getSystemProperties()內是System.getProperties()方法); 接著向propertySources再新增一組屬性,來自系統環境變數(getSystemEnvironment()內是System.getenv()方法); getSystemProperties和getSystemEnvironment方法中有個相同的細節需要注意,在獲取程序變數或者系統環境變數的時候,都有可能因為安全限制丟擲異常,這時候就返回一個ReadOnlySystemAttributesMap的實現類,外部呼叫get方法的時候,再去嘗試獲取程序變數或者系統環境變數對應的值,取不到則返回null,程式碼如下:

public Map