前言
Spring IOC 容器的启动过程是「 定位 -> 加载 -> 注册 -> 实例化 」,前边已经讲解了最重要的「加载」与「注册」过程(「实例化」的内容将在依赖注入的章节讲解),本篇的主题是对「2.1」「2.2」的补充,定位的详细过程会在本篇中讲解,之后如果有必要还会在本篇中补充其他的一些细节内容。
定位
调用过程图↓↓↓
关于前边的调用过程我们略过,直接来到图中的入口: ContextLoader 的 configureAndRefreshWebApplicationContext 方法。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
/**
* [note-by-leapmie]
* CONFIG_LOCATION_PARAM = "contextConfigLocation"
* 对应的含义是读取web.xml中contextConfigLocation的值
*/
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
/** [note-by-leapmie] 调用容器的refresh()方法,此处wac对应的类是XmlWebApplicationContext **/
wac.refresh();
}
在「2.1」 中我们有看过这个方法,当时我们关心的是最后一行 wac.refresh()
,这次我们关心的是wac.setConfigLocation(configLocationParam);
。configLocationParam 的值是调用 sc.getInitParameter(CONFIG_LOCATION_PARAM);
获取的,CONFIG_LOCATION_PARAM的值是 "contextConfigLocation"
,对于 "contextConfigLocation"
有印象吗?回顾我们的 web.xml 配置文件中可以看到,我们配置了一个 <context-param>
上下文参数,这个参数名便是 contextConfigLocation
, 而其值是便是我们自定义的 Spring 配置文件路径
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!-- ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- DispatcherServlet -->
...
</web-app>
如果有兴趣还可以继续深入查看 setConfigLocation 的处理逻辑,其实现过程是在 AbstractRefreshableConfigApplicationContext 类中,通过代码可知道配置文件支持的分隔符有,; \t\n
,这里我们不再占用篇幅深入讲解了。
接下来我们直接分析 XmlWebApplicationContext 的 loadBeanDefinitions(XmlBeanDefinitionReader) 方法(忘记调用路线的可以看回「2.1」中的调用过程图复习一下)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
/** [note-by-leapmie] 获取配置文件位置*/
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
/** [note-by-leapmie] 实际是调用XmlBeanDefinitionReader的loadBeanDefinitions方法 **/
reader.loadBeanDefinitions(configLocation);
}
}
}
这个方法我们在前边也是看过的,当时我们只关注 reader.loadBeanDefinitions(configLocation);
这一行,这次我们关注第一行 String[] configLocations = getConfigLocations();
,从方法的字面意思就很容易理解它的作用——获取配置位置,很明显这就是我们的「定位」过程。
getConfigLocations 的实现方法是在其父类 AbstractRefreshableConfigApplicationContext 中
protected String[] getConfigLocations() {
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}
这个方法的意思是,当容器中的 configLocations 变量为空时则调用 getDefaultConfigLocations, 当不为空时直接返回容器中的 configLocations 。我们先看一看 getDefaultConfigLocations 方法
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
DEFAULT_CONFIG_LOCATION 的定义是
/** Default config location for the root context. */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/WEB-INF/applicationContext.xml
便是我们再熟悉不过的 Spring 默认的配置文件路径与文件名。
而当我们再 web.xml 配置了 contextConfigLocation ,Spring则会读取我们的自定义 Spring 配置文件。至此,关于「定位」的过程已经完整讲解完毕。
原文地址:https://blog.leapmie.com/archives/405/