Spring MVC的初始化逻辑分这么几步:
1、Tomcat扫描监听,就是扫描web.xml中配置的Spring监听;
<!--配置spring listener-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2、Tomcat调用Spring实现的监听接口方法 contextInitialized 进行初始化,初始化的目的就是得到Web应用上下文(WebApplicationContext),初始化的具体逻辑放在父类ContextLoader的方法中,即:initWebApplicationContext;同时Tomcat会把自己的Servlet容器上下文(servletContext)作为参数传给Spring;
3、Spring MVC初始化,得到Web应用上下文(WebApplicationContext),并且把它装配到容器上下文中,细节是放到ServletContext的Map<String,Object> attributes属性中去。
Tomcat: org.apache.catalina.core.ApplicationContext
/**
* The context attributes for this context.
*/
protected Map<String,Object> attributes = new ConcurrentHashMap<>();
@Override
public void setAttribute(String name, Object value) {
//。。。。
Object oldValue = attributes.put(name, value);
//。。。。
}
org.springframework.web.context.ContextLoader
// 这里传入的参数
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//。。。。。
}
Tomcat:org.apache.catalina.core.StandardContext
/**
* Configure the set of instantiated application event listeners
* for this Context.
* @return <code>true</code> if all listeners wre
* initialized successfully, or <code>false</code> otherwise.
*/
public boolean listenerStart() {
//....
// getServletContext()返回ApplicationContextFacade
ServletContextEvent event = new ServletContextEvent(getServletContext());
ServletContextEvent tldEvent = null;
if (noPluggabilityListeners.size() > 0) {
noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
tldEvent = new ServletContextEvent(noPluggabilityServletContext);
}
for (int i = 0; i < instances.length; i++) {
if (!(instances[i] instanceof ServletContextListener))
continue;
ServletContextListener listener =
(ServletContextListener) instances[i];
try {
fireContainerEvent("beforeContextInitialized", listener);
if (noPluggabilityListeners.contains(listener)) {
listener.contextInitialized(tldEvent);
} else {
// 从这里进入Spring逻辑,传的事件包含servletContext
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
fireContainerEvent("afterContextInitialized", listener);
getLogger().error
(sm.getString("standardContext.listenerStart",
instances[i].getClass().getName()), t);
ok = false;
}
}
return (ok);
}
/**
* @return the servlet context for which this Context is a facade.
*/
@Override
public ServletContext getServletContext() {
if (context == null) {
// 直接创建ApplicationContext对象
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
// 这里是getFacade返回,所以上图Spring ContextLoader.initWebApplicationContext方法参数【servletContext】对象的类型是ApplicationContextFacade
return (context.getFacade());
}
Spring MVC初始化:org.springframework.web.context.ContextLoader
/**
* Name of the class path resource (relative to the ContextLoader class)
* that defines ContextLoader's default strategy names.
*/
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);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
/**
* 当前类中定义的Web应用上下文对象,有些文章称之为根应用上下文
* The root WebApplicationContext instance that this loader manages.
*/
private WebApplicationContext context;
// 父类实现初始化Web应用上下文逻辑
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 如果已经又了,报错,从这里就可以先看到初始化后与容器上下文的关系了
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
// 创建WebApplicationContext
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);
}
// 初始化核心方法,参照之前的Spring初始化文章
// 这个cwac是默认的XmlWebApplicationContext对象,它父类AbstractRefreshableApplicationContext中定义了:
// private DefaultListableBeanFactory beanFactory;
// 核心逻辑就是构造BeanFactory付给当前上下文的beanFactory,
// 实现方法在obtainFreshBeanFactory() -> AbstractRefreshableApplicationContext.refreshBeanFactory里面,有兴趣的可以去看看,我这里就不贴了
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//把WebApplicationContext放到ServletContext属性Map中去
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
// 创建web应用上下文
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);
}
// 决定使用哪种实现类
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 {
// 取默认配置的Web应用上下文初始化类,这个类是通过SPI方式提供
// 初始化就在上面的static代码块中
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);
}
}
}
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
综上,项目初始化后的嵌套结构:
容器上下文门面(org.apache.catalina.core.ApplicationContextFacade)
- 容器上下文(org.apache.catalina.core.ApplicationContext)
- Web应用上下文(XmlWebApplicationContext)
- Bean工厂(DefaultListableBeanFactory)
其中:
- ApplicationContextFacade通过context属性引用ApplicationContext,ApplicationContext通过facade引用ApplicationContextFacade;
- 容器上下文(org.apache.catalina.core.ApplicationContext)通过Map<String,Object> attributes存储应用上下文(XmlWebApplicationContext);
- Web应用上下文又包含Bean工厂;
——由此,可以很明确地理解ServletContext,ApplicationContext,BeanFactory三者的关系了:
- ServletContext是容器上下文环境,它是WEB应用的基石或者叫宿主上下文;
- ApplicationContext是应用上下文,有人说它是由BeanFactory派生出来的,我觉得这话不是很恰当,因为应用上下文完全可以不建立在BeanFactory基础之上,只不过呢,它通常都会使用BeanFactory做Bean的维护(这么好的东西干嘛不用);
- BeanFactory则是Spring框架提供的最基础的Bean管理器;如果是Java应用的话,这个BeanFactory的最终类型是:DefaultListableBeanFactory,而包装这个BeanFactory的上下文就是ClassPathXmlApplicationContext,FileSystemXmlApplicationContext等等,这些Context与BeanFactory的引用关系在AbstractRefreshableApplicationContext抽象类中;
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
/** Bean factory for this context */
private DefaultListableBeanFactory beanFactory;
}
我们上面分析的Spring MVC就是其中的一个应用框架,它有自己的上下文环境,当然更多的框架没有自己的上下文环境,而是选择直接让Spring来管理自己的Bean,比如:Mybatis,Durid等等。
这同时也解释了Spring与Spring MVC的关系,Spring提供的BeanFactory,AOP,DI都是其他框架的底层基石,要么在Spring的基础之上增加自己的上下文环境,要么直接使用Spring提供的上下文;