Spring 學習記錄2 Environment


Environment是什么

environment是什么呢....中文是環境大家都知道但是具體代表什么呢?感覺很抽象....從代碼里的解釋來看environment代表了profile和properties.

profile就是1組bean的定義.實際用途就是在不同環境比如測試環境和生產環境中加載不同的bean達到根據環境加載bean的用途.(因為測試環境可能有些bean是模擬的,比如接口.調用返回的報文都是自己模擬的,真實的bean在測試環境拿不到).

properties就不用說了.就是配置...

這篇文章我想分享下我對properties的學習,因為profile我感覺只要會配置就行了...可能更偏向於配置運維,而properties主要涉及到類型轉化等比較復雜的東西.而我自己也寫過一些conveter.所以想特別學習下.

 

結構

大概就是這樣的結構.java web環境下一般都是StandardServletEnvironment環境,而我自己做junit測試的時候是StandardEnvironment 這里主要分析我對StandardEnvironment 的學習(子類可能也就增加了一點點其他功能吧.總的來說應該都是大同小異.估計是把servlet的環境變量也加到properties里了.)

environment的profile的功能是定義在Environment類里的

 1 public interface Environment extends PropertyResolver {
 2 
 3     /**
 4      * Return the set of profiles explicitly made active for this environment. Profiles
 5      * are used for creating logical groupings of bean definitions to be registered
 6      * conditionally, for example based on deployment environment.  Profiles can be
 7      * activated by setting {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
 8      * "spring.profiles.active"} as a system property or by calling
 9      * {@link ConfigurableEnvironment#setActiveProfiles(String...)}.
10      * <p>If no profiles have explicitly been specified as active, then any {@linkplain
11      * #getDefaultProfiles() default profiles} will automatically be activated.
12      * @see #getDefaultProfiles
13      * @see ConfigurableEnvironment#setActiveProfiles
14      * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
15      */
16     String[] getActiveProfiles();
17 
18     /**
19      * Return the set of profiles to be active by default when no active profiles have
20      * been set explicitly.
21      * @see #getActiveProfiles
22      * @see ConfigurableEnvironment#setDefaultProfiles
23      * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
24      */
25     String[] getDefaultProfiles();
26 
27     /**
28      * Return whether one or more of the given profiles is active or, in the case of no
29      * explicit active profiles, whether one or more of the given profiles is included in
30      * the set of default profiles. If a profile begins with '!' the logic is inverted,
31      * i.e. the method will return true if the given profile is <em>not</em> active.
32      * For example, <pre class="code">env.acceptsProfiles("p1", "!p2")</pre> will
33      * return {@code true} if profile 'p1' is active or 'p2' is not active.
34      * @throws IllegalArgumentException if called with zero arguments
35      * or if any profile is {@code null}, empty or whitespace-only
36      * @see #getActiveProfiles
37      * @see #getDefaultProfiles
38      */
39     boolean acceptsProfiles(String... profiles);
40 
41 }
View Code

而properties的功能是定義在PropertyResolver里的

 1 public interface PropertyResolver {
 2 
 3     /**
 4      * Return whether the given property key is available for resolution, i.e.,
 5      * the value for the given key is not {@code null}.
 6      */
 7     boolean containsProperty(String key);
 8 
 9     /**
10      * Return the property value associated with the given key, or {@code null}
11      * if the key cannot be resolved.
12      * @param key the property name to resolve
13      * @see #getProperty(String, String)
14      * @see #getProperty(String, Class)
15      * @see #getRequiredProperty(String)
16      */
17     String getProperty(String key);
18 
19     /**
20      * Return the property value associated with the given key, or
21      * {@code defaultValue} if the key cannot be resolved.
22      * @param key the property name to resolve
23      * @param defaultValue the default value to return if no value is found
24      * @see #getRequiredProperty(String)
25      * @see #getProperty(String, Class)
26      */
27     String getProperty(String key, String defaultValue);
28 
29     /**
30      * Return the property value associated with the given key, or {@code null}
31      * if the key cannot be resolved.
32      * @param key the property name to resolve
33      * @param targetType the expected type of the property value
34      * @see #getRequiredProperty(String, Class)
35      */
36     <T> T getProperty(String key, Class<T> targetType);
37 
38     /**
39      * Return the property value associated with the given key, or
40      * {@code defaultValue} if the key cannot be resolved.
41      * @param key the property name to resolve
42      * @param targetType the expected type of the property value
43      * @param defaultValue the default value to return if no value is found
44      * @see #getRequiredProperty(String, Class)
45      */
46     <T> T getProperty(String key, Class<T> targetType, T defaultValue);
47 
48     /**
49      * Convert the property value associated with the given key to a {@code Class}
50      * of type {@code T} or {@code null} if the key cannot be resolved.
51      * @throws org.springframework.core.convert.ConversionException if class specified
52      * by property value cannot be found  or loaded or if targetType is not assignable
53      * from class specified by property value
54      * @see #getProperty(String, Class)
55      */
56     <T> Class<T> getPropertyAsClass(String key, Class<T> targetType);
57 
58     /**
59      * Return the property value associated with the given key (never {@code null}).
60      * @throws IllegalStateException if the key cannot be resolved
61      * @see #getRequiredProperty(String, Class)
62      */
63     String getRequiredProperty(String key) throws IllegalStateException;
64 
65     /**
66      * Return the property value associated with the given key, converted to the given
67      * targetType (never {@code null}).
68      * @throws IllegalStateException if the given key cannot be resolved
69      */
70     <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
71 
72     /**
73      * Resolve ${...} placeholders in the given text, replacing them with corresponding
74      * property values as resolved by {@link #getProperty}. Unresolvable placeholders with
75      * no default value are ignored and passed through unchanged.
76      * @param text the String to resolve
77      * @return the resolved String (never {@code null})
78      * @throws IllegalArgumentException if given text is {@code null}
79      * @see #resolveRequiredPlaceholders
80      * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String)
81      */
82     String resolvePlaceholders(String text);
83 
84     /**
85      * Resolve ${...} placeholders in the given text, replacing them with corresponding
86      * property values as resolved by {@link #getProperty}. Unresolvable placeholders with
87      * no default value will cause an IllegalArgumentException to be thrown.
88      * @return the resolved String (never {@code null})
89      * @throws IllegalArgumentException if given text is {@code null}
90      * or if any placeholders are unresolvable
91      * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean)
92      */
93     String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
94 
95 }
View Code

讓我覺得很奇怪的一個地方就是既然Environment代表了profile+properties,那為什么profile相關方法要寫在Environment里,而properties相關方法要寫在PropertyResolver里呢...都是@since3.1為什么不再弄個ProfileResolver接口然后Environment接口繼承這2個接口呢?

 

PropertyResolver

從前面的接口代碼觀察.中我覺得這個類主要就是2個作用:

1.給你1個key你要能找到它對應的value.就是解析properties.

2.在properties的基礎上增加了placeholder.value中的一部分可能是占位符,要能根據key找到value同時替換占位符為實際的值.

實驗1,一個小測試:

    
ConfigurablePropertyResolver configurablePropertyResolver; // env

1
/** 2 * getProperty直接寫pro的名字 3 * resolveRequiredPlaceholders用${}替換pro 4 */ 5 @Test 6 public void testPropertiesResolver() { 7 System.out.println("a= " + configurablePropertyResolver.getProperty("a"));//a= b 8 System.out.println("${a}= " + configurablePropertyResolver.getProperty("${a}"));//${a}= null 9 System.out.println("mmp.a= " + configurablePropertyResolver.getProperty("mmp.a"));//mmp.a= 123 10 System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("a=${a}"));//a=b 11 System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("mmp.a=${mmp.a}"));//mmp.a=123 12 }

加載的配置文件

1 a=b
2 mmp.a=123
3 email=jyzjyz12@163.com

從這個測試中可以看出

1.加載完properties配置以后我想獲得value我就只要簡簡單單的調用getProperty方法就行了.

2.比如在bean定義的applicationContext.XML里數據源相關的bean可能會使用占位符定義,比如datasource的username和password <property name="username" value="${jdbc.username}" /> 這里的占位符的解析也是通過propertyResolver, resolveRequiredPlaceholders方法或者resolvePlaceholders等相關placeHolder方法來完成.

上面這么多方法其實總結起來就是讀取了properties文件,通過key得到value就這么簡單...

 

實驗2,再來1個測試:

 1 ConfigurablePropertyResolver configurablePropertyResolver;
 2 
 3     /**
 4      * PropertyPlaceholderHelper 在進test之前就已經初始化完成了,所以修改這個placeHolderPrefix沒用
 5      */
 6     @Test
 7     public void testConfigurablePropertyResolver() {
 8         configurablePropertyResolver.setPlaceholderPrefix("#{");
 9         configurablePropertyResolver.setPlaceholderSuffix("}");
10         System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("郵箱地址=${email}")); //可以被替換
11         System.out.println(configurablePropertyResolver.resolveRequiredPlaceholders("郵箱地址=#{email}"));//無效
12     }

前面測試用到了占位符.默認是${},前綴是${后綴是}......那么我們能不能換個占位符呢? 我們來做1個測試 ↑

這個實驗結果似乎是不能..但是接口明明提供了setPlaceholderPrefix和suffix方法為什么會不行呢?

我們稍微跟下斷點:

1 AbstractEnvironment.java
2 
3     private final ConfigurablePropertyResolver propertyResolver =            new PropertySourcesPropertyResolver(this.propertySources);
4 
5     @Override
6     public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
7         return this.propertyResolver.resolveRequiredPlaceholders(text);
8     }

env里解析placeholder是通過ConfigurablePropertyResolver 來做的.

ConfigurablePropertyResolver 里是用PropertyPlaceholderHelper strictHelper;來做的

1     @Override
2     public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
3         if (this.strictHelper == null) {
4             this.strictHelper = createPlaceholderHelper(false);
5         }
6         return doResolvePlaceholders(text, this.strictHelper);
7     }

然后我再加載spring的XML配置.還沒進入junit的測試方法的時候,需要加載指定目錄下的bean

<context:component-scan base-package="spring">
</context:component-scan>

這樣會導致strictHelper被初始化,ConfigurablePropertyResolver默認的placeholder是${}所以設置到strictHelper里的是${}.

后面進入junit的test方法以后盡管我們去修改了ConfigurablePropertyResolver的placeholder為#{}但是因為strictHelper已經被初始化過了,所以我們並不會重新初始化strictHelper.因此test方法里面修改placeholder為#{}無效.

 

小實驗3:

從實驗2種我們已經知道placeholder最終是PropertyPlaceholderHelper來解析的.那么我們是不是可以直接使用它來設置我們自己的placeholder呢?

 1     /**
 2      * PropertyPlaceholderHelper 替換字符串
 3      */
 4     @Test
 5     public void testConfigurablePropertyResolver2() {
 6         PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("#((", "))");
 7         System.out.println(helper.replacePlaceholders("郵箱地址=#((email))", new PropertyPlaceholderHelper.PlaceholderResolver() { //郵箱地址=jyzjyz12@163.com
 8             @Override
 9             public String resolvePlaceholder(String placeholderName) {
10                 return configurablePropertyResolver.getProperty(placeholderName);
11             }
12         }));
13     }

這里我們新建了1個placeholder是#(())的helper..用它去解析#((email)).....從這個實驗中我們大概可以觀察到.PropertyPlaceholderHelper 得到#((email))這個字符串以后通過匹配前綴和后綴剝離字符串以后肯定會得到email.然后通過email這個key去environment(或者他的委托類的時候)的properties里去getProperties得到對應的value.

我們來稍微跟一下斷點:

當我們調用env的resolveRequiredPlaceholders或者類的其他處理placeholder方法的時候,其實都是通過env內部的PropertyResolver去處理的.就是說env實現了PropertyResolver接口.但是他自己不處理,委托其他類來處理

1 AbstractEnvironment.java
2 
3     private final ConfigurablePropertyResolver propertyResolver =    new PropertySourcesPropertyResolver(this.propertySources);
4     @Override
5     public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
6         return this.propertyResolver.resolveRequiredPlaceholders(text);
7     }
PropertySourcesPropertyResolver是用PropertyPlaceholderHelper來處理,這里分為2步,第一步是helper.replacePlaceholders得到剝離了placeholder的key.第二步是通過內部類的resplvePlaceholder方法調用getPropertyAsRawString方法輸入key得到value
AbstractPropertyResolver.java

    @Override
    public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
        if (this.strictHelper == null) {
            this.strictHelper = createPlaceholderHelper(false);
        }
        return doResolvePlaceholders(text, this.strictHelper);
    }
    private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
        return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
            @Override
            public String resolvePlaceholder(String placeholderName) {
                return getPropertyAsRawString(placeholderName);
            }
        });
    }

PropertyPlaceholderHelper來處理的時候會一層一層剝離placeholder,因為placeholder可能有N層.
 1 /**
 2      * Replaces all placeholders of format {@code ${name}} with the value returned
 3      * from the supplied {@link PlaceholderResolver}.
 4      * @param value the value containing the placeholders to be replaced
 5      * @param placeholderResolver the {@code PlaceholderResolver} to use for replacement
 6      * @return the supplied value with placeholders replaced inline
 7      */
 8     public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
 9         Assert.notNull(value, "'value' must not be null");
10         return parseStringValue(value, placeholderResolver, new HashSet<String>());
11     }
12 
13     protected String parseStringValue(
14             String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
15 
16         StringBuilder result = new StringBuilder(strVal);
17 
18         int startIndex = strVal.indexOf(this.placeholderPrefix);
19         while (startIndex != -1) {
20             int endIndex = findPlaceholderEndIndex(result, startIndex);
21             if (endIndex != -1) {
22                 String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
23                 String originalPlaceholder = placeholder;
24                 if (!visitedPlaceholders.add(originalPlaceholder)) {
25                     throw new IllegalArgumentException(
26                             "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
27                 }
28                 // Recursive invocation, parsing placeholders contained in the placeholder key.
29                 placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
30                 // Now obtain the value for the fully resolved key...
31                 String propVal = placeholderResolver.resolvePlaceholder(placeholder);
32                 if (propVal == null && this.valueSeparator != null) {
33                     int separatorIndex = placeholder.indexOf(this.valueSeparator);
34                     if (separatorIndex != -1) {
35                         String actualPlaceholder = placeholder.substring(0, separatorIndex);
36                         String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
37                         propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
38                         if (propVal == null) {
39                             propVal = defaultValue;
40                         }
41                     }
42                 }
43                 if (propVal != null) {
44                     // Recursive invocation, parsing placeholders contained in the
45                     // previously resolved placeholder value.
46                     propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
47                     result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
48                     if (logger.isTraceEnabled()) {
49                         logger.trace("Resolved placeholder '" + placeholder + "'");
50                     }
51                     startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
52                 }
53                 else if (this.ignoreUnresolvablePlaceholders) {
54                     // Proceed with unprocessed value.
55                     startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
56                 }
57                 else {
58                     throw new IllegalArgumentException("Could not resolve placeholder '" +
59                             placeholder + "'" + " in string value \"" + strVal + "\"");
60                 }
61                 visitedPlaceholders.remove(originalPlaceholder);
62             }
63             else {
64                 startIndex = -1;
65             }
66         }
67 
68         return result.toString();
69     }

29行是個遞歸調用如果我穿的是${email},29行做完返回的就是email...得到了placeholder中的key以后我們就需要通過內部類PropertyPlaceholderHelper.PlaceholderResolver中的getPropertyAsRawString(placeholderName);去通過key得到value.

這個時候的getPropertyAsRawString(placeholderName);中的placeholderName是email.

 

這個方法其實就是簡單的調用getProperty方法

1     @Override
2     protected String getPropertyAsRawString(String key) {
3         return getProperty(key, String.class, false);
4     }

這樣就能獲得value了.

 

小結

小結一下environment解決properties和placeholder的方法.

1.如果是properties.直接通過內部PropertySourcesPropertyResolver的getProperty解決

2.如果是placeholder.通過env內部PropertySourcesPropertyResolver相應的resolveRequiredPlaceholders方法(或者其他placeholder方法)來解決.

2.1.這些方法內部會使用helper來解析placeholder....

2.2.PropertyPlaceholderHelper的replacePlaceholders遞歸調用parseStringValue方法來來剝離placeholder得到key返回給env的PropertySourcesPropertyResolver........

2.3.PropertySourcesPropertyResolver再得到key以后就和查找properties一樣了.

所以placeholder相比properties,就是多了一步解析placeholder得到key.利用了PropertyPlaceholderHelper來處理placeholder.

 

 

以上便是Environment做為PropertyResolver的用途與原理.

 


免責聲明!

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



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