Spring源码私家笔记——容器启动前传:Environment就绪,初始化ApplicationContext

所有尝试分析Spring源码的同学都知道refresh()方法是源码中最为关键的一部分。那么在这之前,都做了什么准备工作,以及对于整个Spring容器启动的意义是什么呢?Spring源码私家笔记——容器启动前传:Environment就绪,初始化ApplicationContext_第1张图片

笔者对这个过程进行了一次简单的梳理(如下图),试图把这部分流程解释清楚。
Spring源码私家笔记——容器启动前传:Environment就绪,初始化ApplicationContext_第2张图片

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
      大家可以稍微留意一下,后面有些还会用到

1.2 getResourcePatternResolver()

获取资源模式解析器,啥意思呢?做两个事情

  1. resourceLoader 用来加载资源,传的就是context对象
  2. 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}.
	 * 

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)的!


笔者原文发布在CSDN,欢迎点击查看:https://blog.csdn.net/mytream/article/details/124568907

也可以关注笔者:请给我一根烟的时间(https://blog.csdn.net/mytream),查看更多个人心得和分享

你可能感兴趣的:(Spring,Spring源码,spring,java,后端)