前面提到了ApplicationContext的執行個體化過程,執行個體化的時候會判斷refresh标志,一般都是true的,在refresh方法中,第一個執行的方法就是prepareRefresh,今天一起看下這個方法内部都做了什麼。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 準備Context的重新整理
prepareRefresh();
....
}
}
prepareRefresh方法分析
- 首先看下ApplicationContext中的prepareRefresh方法内部。
/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
* 準備重新整理,設定應用開啟的時間還有active标志,并且執行一些屬性源的初始化工作
*/
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
// 初始化placeholder屬性
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
// 驗證所有的必須的屬性。
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
// 允許應用啟動之前的事件,當multicaster一旦可用的時候,可用立刻響應釋出的事件。
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
- initPropertySources,在應用啟動之前替換一些屬性占位符,這個方法再Spring的對象中一般都沒有實作,應該是用來友善我們後期擴充使用的方法。
- validateRequiredProperties,Environment類的方法驗證必須的屬性是否正确。
// AbstractEnvironment 類
//private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
// private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
this.propertyResolver.validateRequiredProperties();
}
validateRequiredProperties代碼分析
- 首先看方法内部。
// this.requiredProperties如果沒有操作預設是空的LinkedList
// private final Set<String> requiredProperties = new LinkedHashSet<String>();
public void validateRequiredProperties() {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
for (String key : this.requiredProperties) {
if (this.getProperty(key) == null) {
ex.addMissingRequiredProperty(key);
}
}
if (!ex.getMissingRequiredProperties().isEmpty()) {
throw ex;
}
}
- PropertyResolver.getProperty
// PropertySourcesPropertyResolver類
@Override
public String getProperty(String key) {
return getProperty(key, String.class, true);
}
// 擷取屬性内部真正起作用的方法
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
//打日志
if (logger.isTraceEnabled()) {
logger.trace(String.format("getProperty(\"%s\", %s)", key, targetValueType.getSimpleName()));
}
// this.propertySources上面提到了,傳進來的是個new MutablePropertySources(this.logger),可變的屬性源
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
Object value;
// 從propertySource.getProperty中擷取對應的key,并且進行解析
if ((value = propertySource.getProperty(key)) != null) {
Class<?> valueType = value.getClass();
// 解析字元串類型的key
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
// this.conversionService.canConvert轉換擷取的value
if (!this.conversionService.canConvert(valueType, targetValueType)) {
throw new IllegalArgumentException(String.format(
"Cannot convert value [%s] from source type [%s] to target type [%s]",
value, valueType.getSimpleName(), targetValueType.getSimpleName()));
}
return this.conversionService.convert(value, targetValueType);
}
}
}
if (debugEnabled) {
logger.debug(String.format("Could not find key '%s' in any property source. Returning [null]", key));
}
return null;
}
可以看出PropertySourcesPropertyResolver擷取key的過程是
- 先通過PropertySource擷取value
- 再通過conversionService轉換value為目标類型,預設是轉換為String的是沒有問題的。如果無法轉換抛出異常
小結
ApplicationContext的準備重新整理prepareRefresh邏輯不多,相對簡單一些。不過其中涉及到的解析property的過程需要了解。
其它類
- PropertyPlaceholderHelper ,parseStringValue方法
最後
一步一步來. 看似慢的方法也許是能最快實作目标的方法。