所有尝试分析Spring源码的同学都知道refresh()
方法是源码中最为关键的一部分。那么在这之前,都做了什么准备工作,以及对于整个Spring容器启动的意义是什么呢?
笔者对这个过程进行了一次简单的梳理(如下图),试图把这部分流程解释清楚。
1. super(parent)
我们使用ClassPathXmlApplicationContext进行测试
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"c1.xml", "c1-${user.name}.xml"}, true, null);
User user = (User) context.getBean("user");
user.sayHello();
}
}
super(parent)会调用父类的构造器,此处parent参数会为空,在实际场景中可能有值,比如在spring mvc的spring容器中可能存在父容器的情况。
1.1 初始appContext的一些属性
断点一直跟进去,会发现super()一直往父类方向调用,初始化赋值了一些基础属性,比如:
- AbstractApplicationContext
- logger 日志
- id context的唯一标识
- beanFactoryPostProcessors beanFactory的后置处理器集合
- active context激活标识位
- closed context激活标识位
- startupShutDownMonitor context启动或关闭监听器
- applicationListeners 程序的监听器
- AbstractRefreshableConfigApplicationContext
- setIdCalled
- AbstractXmlApplicationContext
- validating 设置xml文件的验证标志,默认是true
大家可以稍微留意一下,后面有些还会用到
- validating 设置xml文件的验证标志,默认是true
1.2 getResourcePatternResolver()
获取资源模式解析器,啥意思呢?做两个事情
- resourceLoader 用来加载资源,传的就是context对象
- pathMatcher 一个Ant风格的格式匹配器
可以用来加载后面的各种资源,还可以解析资源中出现中的表达式
1.3 setParent(parent)
@Override
public void setParent(@Nullable ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
}
}
}
如果parent非空的话,是需要进行设置,并且将Environment对象进行合并。
2. setConfigLocations
2.1 getEnvironment()
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
/**
* Create and return a new {@link StandardEnvironment}.
* <p>Subclasses may override this method in order to supply
* a custom {@link ConfigurableEnvironment} implementation.
*/
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
此时environment为空,会进行创建 new StandardEnvironment
,创建之后我们发现environment对象就包含了进程的启动参数和系统参数,断点并不会进入赋值逻辑,因为 public StandardEnvironment() {}
构造器为空,默认会调用父类``的构造器
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
我们进入customizePropertySources
模板的扩展实现可以看到将启动参数和系统参数加入进去了。其实spring web的web.xml的启动参数也是这样赋值进来的。
2.2 environment.resolveRequiredPlaceholders(path)
这段逻辑挺繁琐,但做的事情比较好概括,就是对path(配置文件路径)中的ant风格的表达式进行解析,比如笔者的配置文件传的是c1-${user.name}.xml
,就会被解析成c1-笔者电脑的用户名.xml
。这种方式给配置文件的使用也提供了足够的扩展和灵活性。
3. 总结
总结下来,refresh()之前,我们定义了一些context的基础属性(比如标志位、beanFactory的预处理器、监听器集合等),设置资源解析器(用于后续xml的读取和解析),创建了environment(子类通过模板方法子类的扩展加了启动参数和系统变量进去),对配置文件中的表达式做了解析。
此为序幕,后面的章节我们将将着重分析Spring的容器是如何进行启动(Refresh)的!
笔者原文发布在优快云,欢迎点击查看:https://blog.youkuaiyun.com/mytream/article/details/124568907
也可以关注笔者:请给我一根烟的时间(https://blog.youkuaiyun.com/mytream),查看更多个人心得和分享