上一篇文章简单的介绍了一下Spring框架大体的一个执行流程,整个专栏的内容也会根据第一篇序言中的流程图一步一步的向下梳理,并会慢慢补充更多的细节进去。
Test
创建ClassPathXmlApplicationContext来解析xml。
public class Test {
public static void main(String[] args) {
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);
}
setParent(parent)
如果ApplicationContext (第一次进来为null)不为null, 则获取Environment对象,如果Env对象属于ConfigurableEnvironment,则进行merge。
public void setParent(@Nullable ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
}
}
}
getEnvironment
默认创建一个StandardEnvironment对象,SE对象会调用父类AbstractEnvironment构造方法,父类构造方法中,会调用customizePropertySources方法来为属性赋值,由SE实现该方法
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
//getSystemProperties()和getSystemEnvironment()两个方法会获取系统属性和环境
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()));
}
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
因为第一次ApplicationContext为null,所以不会getEnvironment方法,StandardEnvironment对象会在此时进行创建。
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
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;变量此时已经变成了具体的文件名。