Spring源码阅读(一) - 容器创建过程

本文基于 Spring 5.2.8

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");

我们从这段代码出发,看看Spring初始化都做了哪些工作?

构造函数

ClassPathXmlApplicationContext.java

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}


public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

super(parent)

这里主要是调用 AbstractApplicationContext 的静态初始化方法。

public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}

protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}

在下面的解析 xml 路径之前先把 resolver 赋值。

setConfigLocations(configLocations);

/**
* Set the config locations for this application context.
* <p>If not set, the implementation may use a default as appropriate.
*/
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;
}
}

/** AbstractRefreshableConfigApplicationContext.java **/
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}

可以看到,resolvePath 函数实际调用的是 resolveRequiredPlaceholders,即用来解析 placeholder。如果传入的 xml 文件形如 ${xxx} 的格式,通过这一步可以拿到真正的值。

refresh();

这一步是刷新 Spring 容器了,是容器初始化的主流程函数。

Refresh()

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 为刷新做准备工作
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}

prepareRefresh();

protected void prepareRefresh() {
...
// Initialize any placeholder property sources in the context environment.
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
...
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}

主要关注缩略后的代码,initPropertySources 注释上写的是在容器中初始化一些 placeholder property。而 validateRequiredProperties 是验证环境中的一些参数配置。

这两个函数都可以由子类覆写,是 Spring 提供的扩展接口。例如我们可以这样

public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {
super(configLocations);
}

@Override
protected void initPropertySources() {
// 添加验证要求
getEnvironment().setRequiredProperties("VAR");
}
}

那么在 MyClassPathXmlApplicationContext 的容器加载时,就会验证这个环境变量。

参考资料

文章作者: yPhantom
文章链接: https://guoyuxiang.cn/2021/04/06/spring-source1/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Life Note