1.前言
从web.xml解析SpringMvc容器初始化过程,基于Spring4.0+版本
2.web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
/* ContextLoaderListener监听器,当tomcat启动的时候,调用其初始化方法*/
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
/* 当tomcat启动的时候,初始化此servlet*/
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3.ContextLoaderListener(Tomcat启动时会创建)
1、tomcat初始化调用contextInitialized方法(是父类ContextLoader方法)
@Override
public void contextInitialized(ServletContextEvent event) {
/*传入tomcat中servlet容器,即tomcat的上下文环境*/
initWebApplicationContext(event.getServletContext());
}
2、initWebApplicationContext方法(去除了部分日志代码)
这里初始了spring容器
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { /*这里后面WebApplicationContext会放到ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个key对应的value中*/ /*此时servlet容器中已经有WebApplicationContext容器那么报错*/ 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!"); } long startTime = System.currentTimeMillis(); try { /*这个时候肯定为空*/ if (this.context == null) { /*创建WebApplicationContext*/ this.context = createWebApplicationContext(servletContext); } /*if没什么意义,百分百是ConfigurableWebApplicationContext,不太理解*/ if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; /*判断是否刷新过,默认肯定没刷新*/ if (!cwac.isActive()) { /*默认为空*/ if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } /*刷新spring容器*/ configureAndRefreshWebApplicationContext(cwac, servletContext); } } /*将spring容器放入servlet容器中*/ servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); /*使用线程上下文加载,这里设计很棒,如果spring,jar在WEB-inf下那么由WebAppClassLoader 加载*/ /*这里不做分析,读者有兴趣自己可以了解TCCL打破双亲委派原则*/ ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } return this.context; } }
3.调用createWebApplicationContext方法
返回ConfigurableWebApplicationContext对象,本质是XmlWebApplicationContext
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { /*获取到WebApplicationContext实例*/ Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } /*XmlWebApplicationContext父类实现了ConfigurableWebApplicationContext接口所以强转*/ return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }
4.调用determineContextClass方法
如果没有自定义就生成WebApplicationContext实例,加载XmlWebApplicationContext类(其实就是spring容器上下文它解析了默认的配置文件,后期在spring源码解析中分析)
protected Class<?> determineContextClass(ServletContext servletContext) { /*获取web.xml文件中 key为contextClass 的参数,就是可以自定义容器类*/ 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 { /*通过反射实例化webApplicationContext类*/ /*这里defaultStrategies是ContextLoaderListener静态代码块中初始化,会加载一个配置文件*/ /*加载了XmlWebApplicationContext*/ 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); } } }
4.总结
ContextLoaderListener的作用:初始化spring容器,将配置文件中的bean类都刷新实例化(具体实例化过程以后分析)