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 }
而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 }
讓我覺得很奇怪的一個地方就是既然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的用途與原理.