上一篇文章简单的介绍了一下Spring框架大体的一个执行流程,整个专栏的内容也会根据第一篇序言中的流程图一步一步的向下梳理,并会慢慢补充更多的细节进去。
Test
创建ClassPathXmlApplicationContext来解析xml。
public class Test {
public static void main(String[] args) {
//此时的文件名使用了${username}占位符的形式
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-${username}.xml");
}
}
创建BeanFactory
按照序言中的流程图来讲,应该是先读取并解析xml配置文件,但xml的读取完的内容放哪呢? 应该是放在BeanFactory中,所以在解析xml之前应该先创建BeanFactory以及一些初始化的操作。
ClassPathXmlApplicationContext
调用ClassPathXmlApplicationContext构造方法
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 调用父类构造方法,进行相关的对象创建等操作,包含属性的赋值操作
super(parent);
//可能会解析多个配置文件,configLocations是配置文件的字符串数组,设置配置文件路径
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
super(parent)
调用父类构造方法,相关对象进行创建,属性赋值。
//其最上级的父类为AbstractApplicationContext,调用父类的无参构造方法
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
this()
调用父类构造方法,创建PathMatchingResourcePatternResolver来解析XML配置文件
//父类构造方法,创建 资源模式处理器(主要处理XML)
public AbstractApplicationContext() {
// 创建资源模式处理器
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
// 创建一个资源模式解析器(其实就是用来解析xml配置文件)
return new PathMatchingResourcePatternResolver(this);
}
setConfigLocations(configLocations)
设置配置文件路径,因为Test测试类中只传了一个配置文件,所以此时locations = spring-${username}.xml
public void setConfigLocations(@Nullable 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
解析xml路径之前,会先创建一个StandardEnvironment对象,该对象创建时,会在父类构造器中进行customizePropertySources方法的调用,用来获取系统是systemProperties变量和systemEnvironment变量。
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
getEnvironment
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
/**
createEnvironment 会返回一个StandardEnvironment对象
*/
this.environment = createEnvironment();
}
return this.environment;
}
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
resolveRequiredPlaceholders
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
/**
创建一个PropertyPlaceholderHelper对象,用来帮助解析xml路径
*/
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
//创建Helper对象,帮助替换xml文件中的值
//placeholderPrefix:前缀 ${
//placeholderSuffix:后缀 }
//valueSeparator : 值分隔符 :
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
}
doResolvePlaceholders
替换配置文件中的占位符
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
parseStringValue
helper.replacePlaceholders方法会调用此方法进行值的替换,因为是spring-${username}.xml的写法。所以在替换时,会根据getEnvironment中获取的SystemProperties的系统属性username来动态的替换它,替换后,根据个人主机不同,我的主机username为3456,替换后配置文件名为spring-3456.xml。
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
//获取前缀下标
int startIndex = value.indexOf(this.placeholderPrefix);
//看是否包含
if (startIndex == -1) {
return value;
}
StringBuilder result = new StringBuilder(value);
while (startIndex != -1) {
//获取后缀 支持复杂如 spring-${abc${bcd}}形式的写法
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
//如果是复杂方式写法,则递归调用该方法进行解析
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if (visitedPlaceholders == null) {
visitedPlaceholders = new HashSet<>(4);
}
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
if (propVal == null) {
propVal = defaultValue;
}
}
}
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled()) {
logger.trace("Resolved placeholder '" + placeholder + "'");
}
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new IllegalArgumentException("Could not resolve placeholder '" +
placeholder + "'" + " in value \"" + value + "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
}
return result.toString();
}
return返回后,configLocations;变量此时已经变成了具体的文件名。
refresh
创建BeanFacroty前的准备工作,设置容器启动时间,活跃标志位、关闭状态标志位初始值等。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/**
* 前戏,做容器刷新前的准备工作
* 1、设置容器的启动时间
* 2、设置活跃状态为true
* 3、设置关闭状态为false
* 4、获取Environment对象,并加载当前系统的属性值到Environment对象中
* 5、准备监听器和事件的集合对象,默认为空的集合
*/
prepareRefresh();
//省略部分代码。。。。
}
}
}
protected void prepareRefresh() {
// Switch to active.
// 设置容器启动的时间
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());
}
}
// Initialize any placeholder property sources in the context environment.
// 留给子类覆盖,初始化属性资源
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
// 创建并获取环境对象,验证需要的属性文件是否都已经放入环境中,主要是针对上面扩展方法后的校验
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
// 判断刷新前的应用程序监听器集合是否为空,如果为空,则将监听器添加到此集合中
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
// 如果不等于空,则清空集合元素对象
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
// 创建刷新前的监听事件集合
this.earlyApplicationEvents = new LinkedHashSet<>();
}