为了不看晕自己我会把所有的类名都记录下来具体执行到了哪一个类中
ApplicationContext ctx = new ClasspathXmlApplicationContext(“application.xml”) 开始入手看
通过重载构造器发现我们的xml配置文件可以传入多个路径
发现最后调用的是参数最多的这一个
// ClassPathXmlApplicationContext 类
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//调用的是父类的构造器
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
先跟踪super(parent) 发现就做了,一个设置parent操作 而此时传入的parent是null
然后调用了自身空参数的构造器发现
做了两个事情,一个是初始化了一个资源路径匹配解析器
跟踪到父类DefaultResourceLoader空参数构造器 发现就初始化了一个默认的类加载器
// AbstractApplicationContext(类)
/**
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
// 这里还有一条隐藏的super()
this.resourcePatternResolver = getResourcePatternResolver();
//初始化一个资源路径匹配解析器 将自身传入作为资源加载器
// getResourcePatternResolver() = new PathMatchingResourcePatternResolver(this);
}
/**
* Create a new AbstractApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
//初始化一个解析器
this();
//简单的赋值操作将这个parent 赋值到成员变量parent
setParent(parent); //this.parent = parent;
}
// DefaultResourceLoader(类)
public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
}
接下来跟踪setConfigLocations(configLocations);
发现这里就做了一个遍历传入的多个路径,操作验证并且保存存储路径
// AbstractRefreshableConfigApplicationContext(类)
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++) {
//验证并且存储locations
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
接下来跟踪resolvePath(locations[i])
发现这里使用了一个委派模式 解析和验证必须的占位符都不是自己在做,
是委派给了自己的成员变量enviroment在做
// AbstractRefreshableConfigApplicationContext(类)
/**
* 解析给定的路径,将占位符替换为相应的占位符环境属性值。用于配置位置
* @param path
* @return
*/
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
跟踪getEnviroment() 发现是调用的AbstractApplicationContext中的方法
发现最终是new StandardEnviroment(); 创建了一个标准环境
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
跟踪resolveRequiredPlaceholders(path) 方法 发现是调用的父类AbstractEnviroment类中的方法
这里又不是自己干活 又是交给了PropertySourcesPropertyResolver 来操作
//AbstractEnvironment(类)
private final MutablePropertySources propertySources = new MutablePropertySources(logger);
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
继续跟踪 this.propertyResolver.resolveRequiredPlaceholders(text);
发现调用的是AbstractPropertyResolver中的resolveRequiredPlaceholders方法
实际上可以看出AbstractPropertyResolver 和 AbstractEnvironment 都实现了PropertyResolver接口
这里的调用顺序是
AbstractRefreshableConfigApplicationContext –> AbstractEnvironment –> AbstractPropertyResolver –>
PropertyPlaceholderHelper
发现这里真正执行解析的是PropertyPlaceholderHelper 里面的replacePlaceholders方法
//AbstractPropertyResolver (类)
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
// 下面注释的代码等于解释上面这个createPlaceholderHelper(false)
//this.placeholderPrefix = "${";
//this.placeholderSuffix = "}";
//this.valueSeparator = ":"
/*strictHelper = new PropertyPlaceholderHelper
(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, false);*/
}
//真正执行解析path方法
return doResolvePlaceholders(text, this.strictHelper);
}
//使用委派模式 PropertyPlaceholderHelper 去替换占位符
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
//这个this::getPropertyAsRawString 是表示
//new PropertyPlaceholderHelper.PlaceholderResolver(){
// public String resolvePlaceholder(String placeholderName) {
// return getPropertyAsRawString(placeholderName);
// }
// }
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
继续跟踪 helper.replacePlaceholders(text, this::getPropertyAsRawString) 方法
发现这里真正解析实在parseStringValue中解析的,但是看具体实现解析步骤的时候应该知道这个解析应该在后面解析
配置文件会用到,我们会发现一般传入的路径没有${ 所以这里基本上没有做任何操作,直接返回原来的字符串,
到这里这个setConfigLocations(configLocations) 真正的完成了
//AbstractPropertyResolver(类)
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, new HashSet<>());
}
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
//将传入的path -> value 转为builder
StringBuilder result = new StringBuilder(value);
//获取到path中 ${ 的索引位置
int startIndex = value.indexOf(this.placeholderPrefix);
//如果存在${ 这种占位符
while (startIndex != -1) {
//获取到path中} 的索引位置
int endIndex = findPlaceholderEndIndex(result, startIndex);
//如果存在}这种占位符
if (endIndex != -1) {
//截取${}中的字符串
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
//如果存在相同的路径 直接抛出异常
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();
}
总结: 发现就是一系列的初始化操作
在AbstractApplicationContext 中初始化了一个 ResourcePatternResolver
在DefaultResourceLoader 中初始化了一个classloader默认的类加载器 中间还有很多的成员变量也非常有用这里就不一一总结
最后在PropertyPlaceholderHelper发现了一个方法 ParseStringValue 发现我们传入的字符串在这里并没有做什么操作就直接退出了,这里应该解析xml里面的内容还会用到
最后只是将传入的字符串保存到了AbsractRefreshableConfigApplicationContext configLocations数组里面.
(本人小白一枚,如果有哪里说的不对请多多指点,)
下一篇在解析refresh() 的具体操作