本文主要详细分析Spring初始化过程的源码分析,目的是理解Spring具体是如何工作的。部分内容查阅于网络,有不妥之处望指正。
1、web项目中服务器一启动就开始加载web.xml,Spring的启动是从web.xml中的org.springframework.web.context.ContextLoaderListener监听器(上下文加载监听器)开始的。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/bean-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
进入ContextLoaderListener类,该类中主要是上下文初始化方法contextInitialized来初始化上下文。
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
进入initWebApplicationContext方法,主要看下面这部分
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
第二行的createWebApplicationContext(servletContext)是创建上下文的方法,进去查看该方法的源码如下:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
进入determineContextClass(sc)方法,该方法翻译即为“确定上下文类”,也就是说这个类是能确定上下文的实现类。
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
首先从servletContext中查找是否有上下文类名(contextClassName )即这个servletContext.getInitParameter(CONTEXT_CLASS_PARAM),其中CONTEXT_CLASS_PARAM=“contextClass”是个常量,这个方法要做的就是从web.xml中(如下)查找param-name 为contextClass的参数值,即XmlWebApplicationContext类。
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>
</context-param>
如果web.xml中配置了这段就获取这个contextClassName,没有就用默认的策略contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());其中的defaultStrategies对象就是前面声明的,如下:
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
其中用static代码块完成默认的处理方式。ClassPathResource 获取当前路径下的资源文件,从第一行的声明DEFAULT_STRATEGIES_PATH = “ContextLoader.properties”可以看出是叫ContextLoader.properties的文件。如下图:
其中的内容为:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
到此获取到了上下文实现类XmlWebApplicationContext。即 determineContextClass(sc)返回的值。下面再回到createWebApplicationContext方法,该方法获取到上下文实现类后返回return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);返回的就是实例化了这个类的对象。注意此处强制转换为ConfigurableWebApplicationContext接口,该接口是WebApplicationContext接口的子接口,XmlWebApplicationContext实现了ConfigurableWebApplicationContext接口。关系如下
之后就走到了调用createWebApplicationContext方法的initWebApplicationContext方法,运行到这段代码:
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
此时会进入这个if代码段,主要看configureAndRefreshWebApplicationContext(cwac, servletContext)这个方法。这个方法第一步先设置上下文id, 再读取web.xml中配置的contextConfigLocation参数值,进一步设置环境,并加载环境初始化参数(会取web.xml中读取,如果有配置的话)
customizeContext(sc, wac); 方法前主要是自定义初始化,只需要实现接口org.springframework.context.ApplicationContextInitializer
wac.refresh(); 这里才是真正加载xml配置, 这里调用了org.springframework.context.support.AbstractApplicationContext的refresh()。
限于篇幅剩下的部分看第二篇详解。