spring源碼分析系列 (5) spring BeanFactoryPostProcessor拓展類PropertyPlaceholderConfigurer、PropertySourcesP...

更多文章點擊--spring源碼分析系列html

主要分析內容:java

一、拓展類簡述: 拓展類使用demo和自定義替換符號git

二、繼承圖UML解析和源碼分析spring

 (源碼基於spring 5.1.3.RELEASE分析)springboot

 

一、拓展類簡述app

PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer都是個bean工廠後置處理器接口BeanFactoryPostProcessor的實現類。能夠將上下文(配置文件)中的屬性值放在另外一個單獨的標準java Properties文件中去。在XML文件中用${key}替換指定的properties文件中的值或者能夠自定義前綴和後綴。此時只須要對properties文件進 行修改,而不用對xml配置文件進行修改。spring 3.1以後更推薦使用PropertySourcesPlaceholderConfigurer, 由於其更加靈活。PropertyPlaceholderConfigurer一般使用的方式:ide

Bean.java函數

 1 public class Bean {  2 
 3     public Bean(){  4 
 5  }  6 
 7     public Bean(String name){  8         System.out.println("構造函數被調用啦");  9         this.name = name ; 10  } 11 
12     private String name ; 13 
14 
15     public String getName() { 16         return name; 17  } 18 
19     public void setName(String name) { 20         this.name = name; 21  } 22 
23  @Override 24     public String toString() { 25         return "Bean{" +
26                 "name='" + name + '\'' +
27                 '}'; 28  } 29 }
Bean.java

 ioc-PlaceholderConfigurer.xml :工具

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 5     <bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 6         <property name="locations">
 7             <list>
 8                 <value>bean.properties</value>
 9             </list>
10         </property>
11     </bean>
12     <bean id="bean2" class="com.nancy.ioc.Bean">
13         <property name="name" value="${bean.field.name}"/>
14     </bean>
15 </beans>

bean.properties源碼分析

bean.field.name=hello world

 PlaceholderTest.java

 1 public class PlaceholderTest {  2     private ApplicationContext applicationContext ;  3 
 4  @Before  5     public void beforeApplicationContext(){  6         /**
 7  * ApplicationContext 自動註冊 BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor  8  * 不須要手動註冊  9  * */
10         applicationContext = new ClassPathXmlApplicationContext("ioc-PlaceholderConfigurer.xml") ; 11  } 12 
13  @Test 14     public void test(){ 15         Bean bean = applicationContext.getBean("bean2", Bean.class) ; 16  System.out.println(bean); 17  } 18 
19  @After 20     public void after(){ 21  ((ClassPathXmlApplicationContext)applicationContext).close(); 22  } 23 }

 運行結果: name屬性被配置文件對於的值所覆蓋

Bean{name='hello world'}

修改ioc-PlaceholderConfigurer.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-2.5.xsd">

    <bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>bean.properties</value>
            </list>
        </property>
        <!-- 修改前綴-->
        <property name="placeholderPrefix" value="**{"/>
        <property name="placeholderSuffix" value="}**"/>
    </bean>
    <bean id="bean2" class="com.nancy.ioc.Bean">
        <!--<property name="name" value="**{bean.field.name}**:**{bean.field.name2}**"/>-->
        <!--<property name="name" value="${bean.field.name}"/>-->
        <property name="name" value="**{bean.field.name}**"/>
    </bean>

</beans>
ioc-PlaceholderConfigurer.xml

運行結果

Bean{name='hello world'}

 更多示例demo參考: https://gitee.com/zhouxiaoxing91/learning-src/tree/master/spring-src/src/test/java/com/nancy/ioc

 

二、繼承圖UML解析和源碼分析

2.一、PropertyPlaceholderConfigurer繼承圖:

 

 (1)、PropertiesLoaderSupport:抽象類,提供從properties文件中讀取配置信息的能力,該類的屬性locations指定須要加載的文件所在的路徑。mergeProperties()中加載和合並全部的Properties屬性, 可動態控制配置文件和BeanDefinition(默認配置)加載順序.

 1 public abstract class PropertiesLoaderSupport {  2 
 3     /** Logger available to subclasses. */
 4     protected final Log logger = LogFactory.getLog(getClass());  5 
 6     /**
 7  * 本地資源例如從xml加載的配置信息等  8      */
 9  @Nullable  10     protected Properties[] localProperties;  11 
 12     /**
 13  * 爲true則localProperties會覆蓋從locations獲取的屬性值  14      */
 15     protected boolean localOverride = false;  16 
 17     /**
 18  * 配置文件Properties地址  19      */
 20  @Nullable  21     private Resource[] locations;  22 
 23     /**
 24  * 當解析不到對應的配置文件是否報錯  25      */
 26     private boolean ignoreResourceNotFound = false;  27 
 28  @Nullable  29     private String fileEncoding;  30 
 31     /**
 32  * Properties文件加載策略,能夠配置加載編碼形式等. 默認爲DefaultPropertiesPersister,實質委託Properties.load 或者 Properties.store  33      */
 34     private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();  35 
 36     /**
 37  * Set local properties, e.g. via the "props" tag in XML bean definitions.  38  * These can be considered defaults, to be overridden by properties  39  * loaded from files.  40      */
 41     public void setProperties(Properties properties) {  42         this.localProperties = new Properties[] {properties};  43  }  44 
 45     /**
 46  * Set local properties, e.g. via the "props" tag in XML bean definitions,  47  * allowing for merging multiple properties sets into one.  48      */
 49     public void setPropertiesArray(Properties... propertiesArray) {  50         this.localProperties = propertiesArray;  51  }  52 
 53     /**
 54  * Set a location of a properties file to be loaded.  55  * <p>Can point to a classic properties file or to an XML file  56  * that follows JDK 1.5's properties XML format.  57      */
 58     public void setLocation(Resource location) {  59         this.locations = new Resource[] {location};  60  }  61 
 62     /**
 63  * Set locations of properties files to be loaded.  64  * <p>Can point to classic properties files or to XML files  65  * that follow JDK 1.5's properties XML format.  66  * <p>Note: Properties defined in later files will override  67  * properties defined earlier files, in case of overlapping keys.  68  * Hence, make sure that the most specific files are the last  69  * ones in the given list of locations.  70      */
 71     public void setLocations(Resource... locations) {  72         this.locations = locations;  73  }  74 
 75     /**
 76  * Set whether local properties override properties from files.  77  * <p>Default is "false": Properties from files override local defaults.  78  * Can be switched to "true" to let local properties override defaults  79  * from files.  80      */
 81     public void setLocalOverride(boolean localOverride) {  82         this.localOverride = localOverride;  83  }  84 
 85     /**
 86  * Set if failure to find the property resource should be ignored.  87  * <p>"true" is appropriate if the properties file is completely optional.  88  * Default is "false".  89      */
 90     public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {  91         this.ignoreResourceNotFound = ignoreResourceNotFound;  92  }  93 
 94     /**
 95  * Set the encoding to use for parsing properties files.  96  * <p>Default is none, using the {@code java.util.Properties}  97  * default encoding.  98  * <p>Only applies to classic properties files, not to XML files.  99  * @see org.springframework.util.PropertiesPersister#load 100      */
101     public void setFileEncoding(String encoding) { 102         this.fileEncoding = encoding; 103  } 104 
105     /**
106  * Set the PropertiesPersister to use for parsing properties files. 107  * The default is DefaultPropertiesPersister. 108  * @see org.springframework.util.DefaultPropertiesPersister 109      */
110     public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) { 111         this.propertiesPersister =
112                 (propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister()); 113  } 114 
115     /**
116  * 加載和合並全部的Properties屬性, 可動態控制 配置文件屬性 和 BeanDefinition屬性(默認配置) 加載順序 117      */
118     protected Properties mergeProperties() throws IOException { 119         Properties result = new Properties(); 120 
121         if (this.localOverride) { 122             // Load properties from file upfront, to let local properties override.
123  loadProperties(result); 124  } 125 
126         if (this.localProperties != null) { 127             for (Properties localProp : this.localProperties) { 128  CollectionUtils.mergePropertiesIntoMap(localProp, result); 129  } 130  } 131 
132         if (!this.localOverride) { 133             // Load properties from file afterwards, to let those properties override.
134  loadProperties(result); 135  } 136 
137         return result; 138  } 139 
140     /**
141  * 將配置文件信息,加載進入Properties實例 142      */
143     protected void loadProperties(Properties props) throws IOException { 144         if (this.locations != null) { 145             for (Resource location : this.locations) { 146                 if (logger.isTraceEnabled()) { 147                     logger.trace("Loading properties file from " + location); 148  } 149                 try { 150  PropertiesLoaderUtils.fillProperties( 151                             props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister); 152  } 153                 catch (FileNotFoundException | UnknownHostException ex) { 154                     if (this.ignoreResourceNotFound) { 155                         if (logger.isDebugEnabled()) { 156                             logger.debug("Properties resource not found: " + ex.getMessage()); 157  } 158  } 159                     else { 160                         throw ex; 161  } 162  } 163  } 164  } 165  } 166 
167 }
PropertiesLoaderSupport.java

 

(2)、PropertyResourceConfigurer:抽象類, 繼承PropertiesLoaderSupport並實現BeanFactoryPostProcessor接口,資源加載和佔位符替換入口。在postProcessBeanFactory()方法從配置文件中讀取了配置項,並調用抽象方法processProperties()由子類決定處理邏輯。除此以外,提供了convertProperty()方法,該方法是個擴展點,其實裏面什麼都沒作,它能夠用來子類在處理這些配置信息前,對配置信息進行一些轉換,例如配置屬性的解密。

 1 public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport  2         implements BeanFactoryPostProcessor, PriorityOrdered {  3 
 4     private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered
 5 
 6     /**
 7  * BeanFactoryPostProcessor拓展接口, 資源加載和佔位符替換入口  8      */
 9  @Override 10     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 11         try { 12             // 繼承自PropertiesLoaderSupport, 加載配置文件
13             Properties mergedProps = mergeProperties(); 14 
15             // 對已加載屬性配置進行處理, spring一個拓展點. 例如從配置文件獲取密文, 此時能夠作解密
16  convertProperties(mergedProps); 17 
18             // 抽象方法, 由子類實現佔位符替換邏輯
19  processProperties(beanFactory, mergedProps); 20  } 21         catch (IOException ex) { 22             throw new BeanInitializationException("Could not load properties", ex); 23  } 24  } 25  
26     protected void convertProperties(Properties props) { 27         Enumeration<?> propertyNames = props.propertyNames(); 28         while (propertyNames.hasMoreElements()) { 29             String propertyName = (String) propertyNames.nextElement(); 30             String propertyValue = props.getProperty(propertyName); 31             String convertedValue = convertProperty(propertyName, propertyValue); 32             if (!ObjectUtils.nullSafeEquals(propertyValue, convertedValue)) { 33  props.setProperty(propertyName, convertedValue); 34  } 35  } 36  } 37 
38     protected String convertProperty(String propertyName, String propertyValue) { 39         return convertPropertyValue(propertyValue); 40  } 41 
42     /**
43  * 默認返回原始值, 子類重寫實現定製功能, 如:配置文件獲取密文, 此時能夠作解密 44      */
45     protected String convertPropertyValue(String originalValue) { 46         return originalValue; 47  } 48 
49     /**
50  * 抽象方法, 由子類實現佔位符替換邏輯 51      */
52     protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) 53             throws BeansException; 54 
55 }
PropertyResourceConfigurer.java

 

(3)、PlaceholderConfigurerSupport:抽象類,該類持有佔位符符號的前綴、後綴、分隔符,在doProcessProperties()方法實現對BeanDefinition實例中的佔位符進行替換。

 1 public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer  2         implements BeanNameAware, BeanFactoryAware {  3 
 4     /** Default placeholder prefix: {@value}. */
 5     public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";  6 
 7     /** Default placeholder suffix: {@value}. */
 8     public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";  9 
10     /** Default value separator: {@value}. */
11     public static final String DEFAULT_VALUE_SEPARATOR = ":"; 12 
13     /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */
14     protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX; 15 
16     /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX}. */
17     protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX; 18 
19     /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR}. */
20  @Nullable 21     protected String valueSeparator = DEFAULT_VALUE_SEPARATOR; 22 
23     protected boolean trimValues = false; 24 
25  @Nullable 26     protected String nullValue; 27 
28     /**
29  * 當解析不到對應的key是否忽略, 默認爲false. 在配置多個解析類的時候, 可設置爲ture防止屬性加載報錯. 30      */
31     protected boolean ignoreUnresolvablePlaceholders = false; 32 
33  @Nullable 34     private String beanName; 35 
36  @Nullable 37     private BeanFactory beanFactory; 38 
39     // 省略getter 和 setter ........
40 
41     /**
42  * 實現對BeanDefinition實例中的佔位符進行替換 43      */
44     protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, 45  StringValueResolver valueResolver) { 46 
47         /**
48  * 循環得到全部BeanDefinition, 依據模版類StringValueResolver替換對應BeanDefinition的佔位符 49          */
50         BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver); 51 
52         String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames(); 53         for (String curName : beanNames) { 54             // Check that we're not parsing our own bean definition, 55             // to avoid failing on unresolvable placeholders in properties file locations.
56             if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) { 57                 BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName); 58                 try { 59  visitor.visitBeanDefinition(bd); 60  } 61                 catch (Exception ex) { 62                     throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex); 63  } 64  } 65  } 66 
67         // New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
68  beanFactoryToProcess.resolveAliases(valueResolver); 69 
70         // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
71  beanFactoryToProcess.addEmbeddedValueResolver(valueResolver); 72     }
PlaceholderConfigurerSupport.java

 

(4)、PropertyPlaceholderConfigurer:繼承自PlaceholderConfigurerSupport,並實現抽象方法processProperties()。該方法會建立模版類PlaceholderResolvingStringValueResolver,該類提供解析字符串的方法resolveStringValue。建立了StringValueResolver實現類後,交由它的父類PlaceholderConfigurerSupport的doProcessProperties()處理。

  • BeanDefinition:在spring容器初始化時,掃描並獲取每一個bean的聲明(例如在xml中聲明、經過註解聲明等),而後組裝成BeanDefinition,它描述了一個bean實例,擁有屬性值,構造參數值和具體實現提供的其餘信息。
  • BeanDefinitionVisitor:負責訪問BeanDefinition,包括(1)從beanDefinition實例中,獲取spring約定的能夠替換的參數;(2)使用佔位符解析器解析佔位符,並從properties中獲取它對應的值,最後把值設置到BeanDefinition中。
  • PropertyPlaceholderHelper:持有佔位符的前綴、後綴、多值的分隔符,負責把佔位符的字符串去除前綴、後綴. 調用PropertyPlaceholderConfigurerResolver進行字符串替換.
  • PropertyPlaceholderConfigurerResolver:從properties中將傳入佔位符替換爲對應的值. 
 1 public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport {  2 
 3     /** Never check system properties. */
 4     public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;  5 
 6     /**
 7  * Check system properties if not resolvable in the specified properties.  8  * This is the default.  9      */
 10     public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;  11 
 12     /**
 13  * Check system properties first, before trying the specified properties.  14  * This allows system properties to override any other property source.  15      */
 16     public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;  17 
 18     /**
 19  * spring提供工具類, 利用jdk反射機制, 對類常量(public static final)進行映射, 可經過常量名稱進行訪問.  20      */
 21     private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class);  22 
 23     /**
 24  * 三種模式, 默認爲 1:  25  * SYSTEM_PROPERTIES_MODE_NEVER-0: 此時以配置文件爲準,不會加載 JVM系統變量和系統環境變量  26  * SYSTEM_PROPERTIES_MODE_FALLBACK-1: 此時以配置文件優先, 加載不到會再次load JVM系統變量和系統環境變量  27  * SYSTEM_PROPERTIES_MODE_OVERRIDE-2: 此時以JVM系統變量和系統環境變量, 加載不到會再次load 配置文件變量  28      */
 29     private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;  30 
 31     /**
 32  * 控制是否會加載系統環境變量  33      */
 34     private boolean searchSystemEnvironment =
 35             !SpringProperties.getFlag(AbstractEnvironment.IGNORE_GETENV_PROPERTY_NAME);  36 
 37 
 38     /**
 39  * Set the system property mode by the name of the corresponding constant,  40  * e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE".  41  * @param constantName name of the constant  42  * @throws java.lang.IllegalArgumentException if an invalid constant was specified  43  * @see #setSystemPropertiesMode  44      */
 45     public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException {  46         this.systemPropertiesMode = constants.asNumber(constantName).intValue();  47  }  48  
 49     public void setSystemPropertiesMode(int systemPropertiesMode) {  50         this.systemPropertiesMode = systemPropertiesMode;  51  }  52     
 53     public void setSearchSystemEnvironment(boolean searchSystemEnvironment) {  54         this.searchSystemEnvironment = searchSystemEnvironment;  55  }  56 
 57     /**
 58  * 依據systemPropertiesMode配置的策略, 根據佔位符名稱換取對應的值  59      */
 60  @Nullable  61     protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {  62         String propVal = null;  63         if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {  64             propVal = resolveSystemProperty(placeholder);  65  }  66         if (propVal == null) {  67             propVal = resolvePlaceholder(placeholder, props);  68  }  69         if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {  70             propVal = resolveSystemProperty(placeholder);  71  }  72         return propVal;  73  }  74 
 75  @Nullable  76     protected String resolvePlaceholder(String placeholder, Properties props) {  77         return props.getProperty(placeholder);  78  }  79 
 80  @Nullable  81     protected String resolveSystemProperty(String key) {  82         try {  83             String value = System.getProperty(key);  84             // 依據searchSystemEnvironment 判斷是否搜索環境變量
 85             if (value == null && this.searchSystemEnvironment) {  86                 value = System.getenv(key);  87  }  88             return value;  89  }  90         catch (Throwable ex) {  91             if (logger.isDebugEnabled()) {  92                 logger.debug("Could not access system property '" + key + "': " + ex);  93  }  94             return null;  95  }  96  }  97 
 98 
 99     /**
100  * 重寫PlaceholderConfigurerSupport抽象方法processProperties, 實現替換邏輯 101  * Visit each bean definition in the given bean factory and attempt to replace ${...} property 102  * placeholders with values from the given properties. 103      */
104  @Override 105     protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) 106             throws BeansException { 107 
108         StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props); 109  doProcessProperties(beanFactoryToProcess, valueResolver); 110  } 111 
112 
113     /**
114  * 實際替換佔位符的模版類 115      */
116     private class PlaceholderResolvingStringValueResolver implements StringValueResolver { 117 
118         private final PropertyPlaceholderHelper helper; 119 
120         private final PlaceholderResolver resolver; 121 
122         public PlaceholderResolvingStringValueResolver(Properties props) { 123             this.helper = new PropertyPlaceholderHelper( 124  placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders); 125             this.resolver = new PropertyPlaceholderConfigurerResolver(props); 126  } 127 
128  @Override 129  @Nullable 130         public String resolveStringValue(String strVal) throws BeansException { 131             String resolved = this.helper.replacePlaceholders(strVal, this.resolver); 132             if (trimValues) { 133                 resolved = resolved.trim(); 134  } 135             return (resolved.equals(nullValue) ? null : resolved); 136  } 137  } 138 
139     private final class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver { 140 
141         private final Properties props; 142 
143         private PropertyPlaceholderConfigurerResolver(Properties props) { 144             this.props = props; 145  } 146 
147  @Override 148  @Nullable 149         public String resolvePlaceholder(String placeholderName) { 150             return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, 151                     this.props, systemPropertiesMode); 152  } 153  } 154 
155 }
PropertyPlaceholderConfigurer.java

 

2.二、PropertySourcesPlaceholderConfigurer繼承圖:

 

 PropertySourcesPlaceholderConfigurer繼承體系和PropertyPlaceholderConfigurer相似, 這裏只總結變化的部分:

  • PropertySourcesPlaceholderConfigurer會將Properties轉換爲屬性集合PropertySources,以解析替換全部佔位符.而PropertyPlaceholderConfigurer使用原始JDK Properties
  • 解析的來源包含: mergeProperties獲取配置文件中的properties 和 Environment環境變量中的properties
  • 經過localOverride 控制加載的前後順序
  •  一旦setPropertySources設置了propertySources屬性資源,其餘的資源將會被忽略.以此達到用戶更細粒度控制資源加載

 PropertySourcesPlaceholderConfigurer源碼解析

 1 public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {  2 
 3     // 一、標識不一樣來源的屬性配置, 從配置文件獲取 或者 環境變量獲取和系統變量(好比springboot中applications.properties定義屬性)
 4     /**
 5  * {@value} is the name given to the {@link PropertySource} for the set of  6  * {@linkplain #mergeProperties() merged properties} supplied to this configurer.  7      */
 8     public static final String LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME = "localProperties";  9 
 10     /**
 11  * {@value} is the name given to the {@link PropertySource} that wraps the  12  * {@linkplain #setEnvironment environment} supplied to this configurer.  13      */
 14     public static final String ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME = "environmentProperties";  15 
 16  @Nullable  17     private MutablePropertySources propertySources;  18 
 19  @Nullable  20     private PropertySources appliedPropertySources;  21 
 22  @Nullable  23     private Environment environment;  24 
 25 
 26     /**
 27  * Customize the set of {@link PropertySources} to be used by this configurer.  28  * <p>Setting this property indicates that environment property sources and  29  * local properties should be ignored.  30  * @see #postProcessBeanFactory  31      */
 32     public void setPropertySources(PropertySources propertySources) {  33         this.propertySources = new MutablePropertySources(propertySources);  34  }  35 
 36     /**
 37  * {@code PropertySources} from the given {@link Environment}  38  * will be searched when replacing ${...} placeholders.  39  * @see #setPropertySources  40  * @see #postProcessBeanFactory  41      */
 42  @Override  43     public void setEnvironment(Environment environment) {  44         this.environment = environment;  45  }  46 
 47 
 48     // PropertySourcesPlaceholderConfigurer會將Properties轉換爲屬性集合PropertySources,以解析替換全部佔位符.而PropertyPlaceholderConfigurer使用原始JDK Properties  49     // 解析的來源包含: mergeProperties獲取配置文件中的properties 和 Environment環境變量中的properties  50     // 也是經過localOverride 控制加載的前後順序  51     // 一旦setPropertySources設置了propertySources屬性資源,其餘的資源將會被忽略.以此達到用戶更細粒度控制資源加載
 52     /**
 53  * Processing occurs by replacing ${...} placeholders in bean definitions by resolving each  54  * against this configurer's set of {@link PropertySources}, which includes:  55  * <ul>  56  * <li>all {@linkplain org.springframework.core.env.ConfigurableEnvironment#getPropertySources  57  * environment property sources}, if an {@code Environment} {@linkplain #setEnvironment is present}  58  * <li>{@linkplain #mergeProperties merged local properties}, if {@linkplain #setLocation any}  59  * {@linkplain #setLocations have} {@linkplain #setProperties been}  60  * {@linkplain #setPropertiesArray specified}  61  * <li>any property sources set by calling {@link #setPropertySources}  62  * </ul>  63  * <p>If {@link #setPropertySources} is called, <strong>environment and local properties will be  64  * ignored</strong>. This method is designed to give the user fine-grained control over property  65  * sources, and once set, the configurer makes no assumptions about adding additional sources.  66      */
 67  @Override  68     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  69         if (this.propertySources == null) {  70             this.propertySources = new MutablePropertySources();  71             if (this.environment != null) {  72                 this.propertySources.addLast(  73                     new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {  74  @Override  75  @Nullable  76                         public String getProperty(String key) {  77                             return this.source.getProperty(key);  78  }  79  }  80  );  81  }  82             try {  83                 PropertySource<?> localPropertySource =
 84                         new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());  85                 if (this.localOverride) {  86                     this.propertySources.addFirst(localPropertySource);  87  }  88                 else {  89                     this.propertySources.addLast(localPropertySource);  90  }  91  }  92             catch (IOException ex) {  93                 throw new BeanInitializationException("Could not load properties", ex);  94  }  95  }  96 
 97         processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));  98         this.appliedPropertySources = this.propertySources;  99  } 100 
101     /**
102  * Visit each bean definition in the given bean factory and attempt to replace ${...} property 103  * placeholders with values from the given properties. 104      */
105     protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, 106             final ConfigurablePropertyResolver propertyResolver) throws BeansException { 107 
108         // 區分於PropertyPlaceholderConfigurer, 使用ConfigurablePropertyResolver實例持有佔位符的前綴、後綴、多值的分隔符 已經進行字符串替換
109         propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); 110         propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); 111         propertyResolver.setValueSeparator(this.valueSeparator); 112 
113         StringValueResolver valueResolver = strVal -> { 114             String resolved = (this.ignoreUnresolvablePlaceholders ?
115  propertyResolver.resolvePlaceholders(strVal) : 116  propertyResolver.resolveRequiredPlaceholders(strVal)); 117             if (this.trimValues) { 118                 resolved = resolved.trim(); 119  } 120             return (resolved.equals(this.nullValue) ? null : resolved); 121  }; 122 
123         // 委託抽象類PlaceholderConfigurerSupport#doProcessProperties解析,同PropertyPlaceholderConfigurer同樣
124  doProcessProperties(beanFactoryToProcess, valueResolver); 125  } 126 
127     // 此時已廢棄PlaceholderConfigurerSupport#processProperties
128     /**
129  * Implemented for compatibility with 130  * {@link org.springframework.beans.factory.config.PlaceholderConfigurerSupport}. 131  * @deprecated in favor of 132  * {@link #processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver)} 133  * @throws UnsupportedOperationException in this implementation 134      */
135  @Override 136  @Deprecated 137     protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) { 138         throw new UnsupportedOperationException( 139                 "Call processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver) instead"); 140  } 141 
142     /**
143  * Return the property sources that were actually applied during 144  * {@link #postProcessBeanFactory(ConfigurableListableBeanFactory) post-processing}. 145  * @return the property sources that were applied 146  * @throws IllegalStateException if the property sources have not yet been applied 147  * @since 4.0 148      */
149     public PropertySources getAppliedPropertySources() throws IllegalStateException { 150         Assert.state(this.appliedPropertySources != null, "PropertySources have not yet been applied"); 151         return this.appliedPropertySources; 152  } 153 }
PropertySourcesPlaceholderConfigurer.java
相關文章
相關標籤/搜索