spring-core源碼解析-(1)
基本
本部分從最基本的Spring開始铆农。配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean class="base.SimpleBean"></bean>
</beans>
啟動代碼:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
SimpleBean bean = context.getBean(SimpleBean.class);
bean.send();
context.close();
}
SimpleBean:
public class SimpleBean {
public void send() {
System.out.println("I am send method from SimpleBean!");
}
}
ClassPathXmlApplicationContext
整個繼承體系如下:
ResourceLoader代表了加載資源的一種方式伸蚯,正是策略模式的實現(xiàn)。
構造器源碼:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) {
super(parent);
setConfigLocations(configLocations);
//默認true
if (refresh) {
refresh();
}
}
構造器
首先看父類構造器晾嘶,沿著繼承體系一直向上調(diào)用熟吏,直到AbstractApplicationContext:
public AbstractApplicationContext(ApplicationContext parent) {
this();
setParent(parent);
}
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
getResourcePatternResolver:
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
PathMatchingResourcePatternResolver支持Ant風格的路徑解析距糖。
設置配置文件路徑
即AbstractRefreshableConfigApplicationContext.setConfigLocations:
public void setConfigLocations(String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}
resolvePath:
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
此方法的目的在于將占位符(placeholder)解析成實際的地址玄窝。比如可以這么寫: new ClassPathXmlApplicationContext("classpath:config.xml");
那么classpath:就是需要被解析的。
getEnvironment方法來自于ConfigurableApplicationContext接口悍引,源碼很簡單恩脂,如果為空就調(diào)用createEnvironment創(chuàng)建一個。AbstractApplicationContext.createEnvironment:
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
Environment接口
StandardEnvironment繼承體系:
Environmen接口代表了當前應用所處的環(huán)境趣斤。從此接口的方法可以看出俩块,其主要和profile、Property相關浓领。
Profile
Spring Profile特性是從3.1開始的玉凯,其主要是為了解決這樣一種問題: 線上環(huán)境和測試環(huán)境使用不同的配置或是數(shù)據(jù)庫或是其它。有了Profile便可以在 不同環(huán)境之間無縫切換联贩。Spring容器管理的所有bean都是和一個profile綁定在一起的壮啊。使用了Profile的配置文件示例:
<beans profile="develop">
<context:property-placeholder location="classpath*:jdbc-develop.properties"/>
</beans>
<beans profile="production">
<context:property-placeholder location="classpath*:jdbc-production.properties"/>
</beans>
<beans profile="test">
<context:property-placeholder location="classpath*:jdbc-test.properties"/>
</beans>
在啟動代碼中可以用如下代碼設置活躍(當前使用的)Profile:
context.getEnvironment().setActiveProfiles("dev");
當然使用的方式還有很多(比如注解),參考:
spring3.1 profile 配置不同的環(huán)境
Property
這里的Property指的是程序運行時的一些參數(shù)撑蒜,引用注釋:
properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,Maps, and so on.
Environment構造器
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
PropertySources接口
繼承體系:
此接口實際上是PropertySource的容器歹啼,默認的MutablePropertySources實現(xiàn)內(nèi)部含有一個CopyOnWriteArrayList作為存儲載體。
StandardEnvironment.customizePropertySources:
/** System environment property source name: {@value} */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value} */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@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()));
}
PropertySource接口
PropertySource接口代表了鍵值對的Property來源座菠。繼承體系:
AbstractEnvironment.getSystemProperties:
@Override
public Map<String, Object> getSystemProperties() {
try {
return (Map) System.getProperties();
}
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(attributeName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info(format("Caught AccessControlException when accessing system " +
"property [%s]; its value will be returned [null]. Reason: %s",
attributeName, ex.getMessage()));
}
return null;
}
}
};
}
}
這里的實現(xiàn)很有意思狸眼,如果安全管理器阻止獲取全部的系統(tǒng)屬性,那么會嘗試獲取單個屬性的可能性浴滴,如果還不行就拋異常了拓萌。
getSystemEnvironment方法也是一個套路,不過最終調(diào)用的是System.getenv升略,可以獲取jvm和OS的一些版本信息微王。
路徑Placeholder處理
AbstractEnvironment.resolveRequiredPlaceholders:
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
//text即配置文件路徑,比如classpath:config.xml
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
propertyResolver是一個PropertySourcesPropertyResolver對象:
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
PropertyResolver接口
PropertySourcesPropertyResolver繼承體系(排除Environment分支):
此接口正是用來解析PropertyResource品嚣。
解析
AbstractPropertyResolver.resolveRequiredPlaceholders:
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
//三個參數(shù)分別是${, }, :
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
}
doResolvePlaceholders:
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
//PlaceholderResolver接口依然是策略模式的體現(xiàn)
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
@Override
public String resolvePlaceholder(String placeholderName) {
return getPropertyAsRawString(placeholderName);
}
});
}
其實代碼執(zhí)行到這里的時候還沒有進行xml配置文件的解析炕倘,那么這里的解析placeHolder是什么意思呢,原因在于可以這么寫:
System.setProperty("spring", "classpath");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("${spring}:config.xml");
SimpleBean bean = context.getBean(SimpleBean.class);
這樣就可以正確解析翰撑。placeholder的替換其實就是字符串操作罩旋,這里只說一下正確的屬性是怎么來的。實現(xiàn)的關鍵在于PropertySourcesPropertyResolver.getProperty:
@Override
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
Object value = propertySource.getProperty(key);
return value;
}
}
return null;
}
很明顯了眶诈,就是從System.getProperty和System.getenv獲取涨醋,但是由于環(huán)境變量是無法自定義的,所以其實此處只能通過System.setProperty指定逝撬。
注意浴骂,classpath:XXX這種寫法的classpath前綴到目前為止還沒有被處理。
關注公眾號: 太上老君007
spring源碼分析第一時間通知你