Spring IoC原理(三) prepareRefresh()方法
一、prepareRefresh()方法分析
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
initPropertySources();
getEnvironment().validateRequiredProperties();
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
this.earlyApplicationEvents = new LinkedHashSet<>();
}
二、initPropertySources()
??首先先来看一下initPropertySources()方法的源码,从源码中可以看到注明对于子类默认情况下不执行任何操作,但可以发现该方法被三个子类进行重写,如图2-1所示。要了解initPropertySources()方法的作用,先要了解什么是PropertySource。
protected void initPropertySources() {
}
图2-1 重写initPropertySources()方法的子类
?? ??下面是PropertySource类的代码,其中部分方法已经省略。从代码中可以看出,PropertySource共有两个私有属性,name和source,其中source的类型可以是Properties、Map、ServletContext、ServletConfig,获取source可通过调用getSource()方法。在PropertySource类中有一个静态内部类StubPropertySource,该类主要起占位作用,主要解决source来源无法在上下文创建期间预先初始化的问题,由于该类被static修饰,因此在类加载完成后即可完成实例化。例如,一个基于ServletContext的source来源在上下文环境创建的过程中无法直接使用,因此通过StubPropertySource类完成占位,在进行上下文刷新过程中再完成替换,也就是上面的initPropertySources()方法的作用。
??为了便于操作PropertySource对象,Spring中有PropertySources接口与MutablePropertySources类,PropertySources提供了一个类型为PropertySource的迭代器,提供了一些MutablePropertySources在PropertySources基础上进行了功能上的扩展, 提供一些类似add、replace方法。
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
protected final String name;
protected final T source;
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
@Override
@Nullable
public String getProperty(String name) {
return null;
}
}
static class ComparisonPropertySource extends StubPropertySource {
private static final String USAGE_ERROR =
"ComparisonPropertySource instances are for use with collection comparison only";
public ComparisonPropertySource(String name) {
super(name);
}
@Override
public Object getSource() {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public boolean containsProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
@Nullable
public String getProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
}
}
三、validateRequiredProperties()
??在调用validateRequiredProperties()首先调用getEnvironment()方法,可以看一下getEnvironment()方法的源码,若当前环境为空则创建一个标准环境StandardEnvironment,然后将返回当前环境。
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
??接着调用返回的环境StandardEnvironment基类AbstractEnvironment中的validateRequiredProperties()方法进行属性验证。在上下文环境创建前可通过ConfigurablePropertyResolver的setRequiredProperties()方法对必要属性进行设置并存储在requiredProperties中,validate RequiredProperties()方法会遍历requiredProperties中的所有属性检测是否进行相应的设置,若存在未设置的属性则会抛出异常。
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;
}
}
|