1. 程式人生 > >spring學習(一)屬性管理PropertySource類

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;
	}