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


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

主要分析內容:

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

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

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

 

1、拓展類簡述

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

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

 

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

2.1、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.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     // 1、標識不同來源的屬性配置, 從配置文件獲取 或者 環境變量獲取和系統變量(比如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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM