spring Bean加载过程
1、找准入口 ,使用ClassPathXmlApplicationContext加载配置文件,用于加载classPath下的配置文件
//第一行,执行完成之后就完成了spring配置文件的加载,刷新spring上下文
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext(
"classpath:spring-mvc.xml");
//获取实例Bean
Person person=context.getBean("person",Person.class);
ClassPathXmlApplicationContext的继承关系如下:
https://images2015.cnblogs.com/blog/801753/201702/801753-20170201125310058-568989522.png
2、现在开始仔细分析第一句,可以看出第一句就已经完成了spring配置文件的加载
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext(
"classpath:spring-mvc.xml");
3、查看classPathXmlApplicationContext的源码,下面表格是对象
对象名 | 类 型 | 作 用 | 归属类 |
---|---|---|---|
configResources | Resource[] | 配置文件资源对象数组 | ClassPathXmlApplicationContext |
configLocations | String[] | 配置文件字符串数组,存储配置文件路径 | AbstractRefreshableConfigApplicationContext |
beanFactory | DefaultListableBeanFactory | 上下文使用的Bean工厂 | AbstractRefreshableApplicationContext |
beanFactoryMonitor | Object | Bean工厂使用的同步监视器 | AbstractRefreshableApplicationContext |
id | String | 上下文使用的唯一Id,标识此ApplicationContext | AbstractApplicationContext |
parent | ApplicationContext | 父级ApplicationContext | AbstractApplicationContext |
beanFactoryPostProcessors | List<BeanFactoryPostProcessor> |
存储BeanFactoryPostProcessor接口,Spring提供的一个扩展点 | AbstractApplicationContext |
startupShutdownMonitor | Object | refresh方法和destory方法公用的一个监视器,避免两个方法同时执行 | AbstractApplicationContext |
shutdownHook | Thread | Spring提供的一个钩子,JVM停止执行时会运行Thread里面的方法 | AbstractApplicationContext |
resourcePatternResolver | ResourcePatternResolver | 上下文使用的资源格式解析器 | AbstractApplicationContext |
lifecycleProcessor | LifecycleProcessor | 用于管理Bean生命周期的生命周期处理器接口 | AbstractApplicationContext |
messageSource | MessageSource | 用于实现国际化的一个接口 | AbstractApplicationContext |
applicationEventMulticaster | ApplicationEventMulticaster | Spring提供的事件管理机制中的事件多播器接口 | AbstractApplicationContext |
applicationListeners | Set<ApplicationListener> | Spring提供的事件管理机制中的应用监听器 | AbstractApplicationContext |
4、从构造方法可以看出,加载spring配置文件实际调用的是如下构造方法:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
//设置父级的ApplicationContext,null
super(parent);
//1.设置配置文件的路径, 2. 将路径中的占位符${placeholder}使用系统的变量替换
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
5、进入setConfigLocations(configLocations);
的源码,这个方法是父类AbstractRefreshableConfigApplicationContext中的方法
1. 设置配置文件的路径
2. 替换路径中的占位符`${placeholder}`为系统变量中的值
//locations : 配置文件路径-+
public void setConfigLocations(String[] locations) {
if (locations != null) {
//断言
Assert.noNullElements(locations, "Config locations must not be null");
//存储配置文件路径的数组,存储去掉占位符后的文件路径数组
this.configLocations = new String[locations.length];
//遍历locations,解析占位符
for (int i = 0; i < locations.length; i++) {
//调用resolvePath解析占位符
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
6、进入resolvePath
的源码可以知道,实际上执行的是AbstractPropertyResolver
的doResolvePlaceholders
方法,如下
/**
* text : 需要解析的路径
* PropertyPlaceholderHelper : 这个是解析系统占位符的辅助类,主要用来将占位符替换成系统的环境变量
*/
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
//调用PropertyPlaceholderHelper类中的replacePlaceholders方法
return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
public String resolvePlaceholder(String placeholderName) {
return getPropertyAsRawString(placeholderName);
}
});
}
7、进入PropertyHelpe
r中的replacePlaceholders
方法,实际上调用org.springframework.util.PropertyPlaceholderHelper
这个类的parseStringValue
解析占位符
- 实际调用的是parseStringValue方法
- this.placeholderPrefix这个是占位符的前缀 {" ,placeholderSuffix="}",valueSeparator=":
- 使用parseStringValue方法递归解析占位符中的内容
- 在
parseStringValue
方法中使用两次递归placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
,这个是第一次,用来解析占位符中的placeholder是否还包含占位符,如果有占位符需要将其抽离出来,去掉${}
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
,这个是第二次递归调用,用来解析propVal
中的占位符
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "Argument 'value' must not be null.");
//调用的是parseStringValue方法
return parseStringValue(value, placeholderResolver, new HashSet<String>());
}
/**
* strVal : 需要解析的字符串,就是配置文件的路径
* placeholderResolver : 策略接口,占位符解析器
* visitedPlaceholders : 存储已经访问过的占位符
**/
protected String parseStringValue(
String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
//将st