spring學習(一)屬性管理PropertySource類
一、PropertySource:用於存放key-value物件的抽象,子類需要實現getProperty(String name)返回對應的Value方法,其中value可以是任何型別不侷限在字串
注:PropertySource裡的屬性name和source都是final的。初始化後不能修改
其中named(String name)是用來判斷數組裡是否包括當前name的方法,spring給的例子如下
@Test public void testSource() { List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>(); sources.add(new MapPropertySource("sourceA", new HashMap<String, Object>())); sources.add(new MapPropertySource("sourceB", new HashMap<String, Object>())); Assert.assertEquals(true, sources.contains(PropertySource.named("sourceA"))); Assert.assertEquals(true, sources.contains(PropertySource.named("sourceB"))); Assert.assertEquals(false, sources.contains(PropertySource.named("sourceC"))); }
PropertySource.named的方法實現
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}
其中ComparisonPropertySource只對List有用,只繼承了getName方法。其中方法都會丟擲異常
ComparisonPropertySource instances are for collection comparison use only
對應子類
EnumerablePropertySource:增加了一個方法用於返回所有name值getPropertyNames,同時重寫的containsProperty方法,通過getPropertyNames返回的key值進行判斷,有助於提升效能
MapPropertySource:其中的source是以Map形式存放的
重寫了getProperty和getPropertyNames
PropertiesPropertySoruce:同MapPropertySource,只是建構函式的引數不同
二、PropertySources:用於存放PropertySource的集合
MutablePropertySources:用linkList實現PropertySources,可以方便向List鏈中首位、末位、中間位置增加或替換或刪除一個key-value屬性值
每次增加或替換時,都會判斷這個PropertySource是否存在,如果存在,先刪除。保證整個List中name的唯一
剛開始看原始碼,MapPropertySource和MutablePropertySources總是容易弄混。慢慢看了一些應用的原始碼。比如spring在預啟動時,會載入系統的環境變數。StandardEnvironment程式碼如下
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
父類AbstractEnvironment程式碼如下
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
此時的環境變數結構如下圖表示,而StandardEnvironment.getProperty(key)是需要遍歷MutablePropertySources裡面的所有PropertySource,並檢視是否有存在的key值
三,PropertySource應用
spring在載入通過xml檔案配置的bean檔案,可以在檔案路徑地址中寫入一些系統引數資訊,比如例子如下(可能配置的沒什麼意義)
public class TestPropertyEditorRegistrar {
private ClassPathXmlApplicationContext context;
@Before
public void setUp() throws Exception {
context = new ClassPathXmlApplicationContext(new String[] { "classpath*:spring/${java.vm.version}/propertyEditor.xml" });
}
}
其中的${java.vm.version}會在正式載入之前通過StandardEnvironment替換成相應的變數名稱
1)在ClassPathXmlApplicationContext的父類中AbstractRefreshableConfigApplicationContext中的setConfigLocations(String ... locations)有如下方法
protected void setConfigLocations(String[] locations) {
if (locations != null) {
Assert.notNullElements(locations, "Config locations must not be null");
configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
configLocations[i] = resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}
protected String resolvePath(String path) {
//呼叫PropertyResolver.resolveRequiredPlaceholders的方法,
//其中getEnvironment得到的物件是StandardEnvironment,而這個方法是由父類AbstractEnvironment中實現
return getEnvironment().resolveRequiredPlaceholders(path);
}
2)AbstractEnvironment實現了Envirionment和PropertyResolver介面,但對於PropertyResolver的介面中的方法都是通過PropertySourcesPropertyResolver類實現的,PropertySourcesPropertyResolver這個同PropertyResolver的一個子類
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
//呼叫介面PropertyResolver進行具體的解析字串.類PropertySourcesPropertyResolver的物件生成時會傳遞一個MutablePropertySources物件
//StandardEnvironment的的實現傳遞了2個PropertySource,(systemEnvironment,systemProperties)
//最終呼叫的是AbstractPropertyResolver.resolveRequiredPlaceholders方法
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
3)AbstractPropertyResolver.resolveRequiredPlaceholders的關鍵實現類是PropertyPlaceholderHelper.replacePlaceholders方法
public String resolveRequiredPlaceholders(String value) {
if (strictHelper == null) {
strictHelper = createPlaceholderHelper(true);
}
return doResolvePlaceholders(value, strictHelper);
}
/**
* 定義一個PropertyPlaceholderHelper,並傳引數用於判斷是否忽略不能解析的變數
*/
public PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvableNestedPlaceholders) {
return new PropertyPlaceholderHelper(placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvableNestedPlaceholders);
}
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
//直接呼叫helper.parseStringValue方法,這個方法也得spring用來解析字串中所有替換變數實現類,
//這個方法的實現的可以單獨說明,主要功能是替換text中的${xxxx}指導xxxx替換成getProperty(placeholderName)取到的變數值
return helper.parseStringValue(text, new PlaceHolderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
//這個方法是由具體的PropertyResolver的子類實現的
return getProperty(placeholderName);
}
});
}
4)PropertyResolver.getProperty(name)的實現,這裡是由PropertySourcePropertyResolver類實現,並支援巢狀替換
@Override
public String getProperty(String key) {
if (logger.isTraceEnabled()) {
logger.trace(format("getProperty(\"%s\")(implicit targetType [String])", key));
}
return getProperty(key, String.class);
}
public <T> T getProperty(String key, Class<T> requiredType) {
boolean debugEnabled = logger.isDebugEnabled();
if (logger.isTraceEnabled()) {
logger.trace(format("getProperty(\"%s\", %s)", key, requiredType.getSimpleName()));
}
for (PropertySource<?> propertySource : propertySources) {
if (debugEnabled) {
logger.debug(format("Searching for key '%s' in [%s]", key, propertySource.getName()));
}
Object value;
if ((value = propertySource.getProperty(key)) != null) {
Class<?> valueType = value.getClass();
//如果型別是strng,可以巢狀解析
if (String.class.equals(valueType)) {
value = this.resolveNestedPlaceholders((String)value);
}
if (debugEnabled) {
logger.debug(
format("Found key '%s' in [%s] with type [%s] and value '%s'",
key, propertySource.getName(), valueType.getSimpleName(), value));
}
return (T)value;
}
}
if (debugEnabled) {
logger.debug(format("Could not find key '%s' in any property source. Returning [null]", key));
}
return null;
}