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>
<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方法
@Override
public void contextInitialized(ServletContextEvent event) {
/*传入Tomcat中servlet容器,即tomcat的上下文环境*/
initWebApplicationContext(event.getServletContext());
}
2、initWebApplicationContext方法(去除了部分日志代码)
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
/*防止xml中多次使用监听器初始化*/
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException("error");
}
try {
if (this.context == null) {
/*创建IOC容器*/
this.context = createWebApplicationContext(servletContext);
}
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);
}
}
/*将IOC容器放入servlet容器中*/
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);
}
return this.context;
}
}
initWebApplicationContext做了如下:
(1)调用createWebApplicationContext方法 创建springIOC容器
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { // 获取需要创建的容器class文件地址 Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("error"); } // 反射生成实例 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }
protected Class<?> determineContextClass(ServletContext servletContext) { // 加载web.xml中初始化参数是否有contextClass,如果有使用此值作为contextClass String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } } else { // 加载配置文件中默认contextClass(XmlWebApplicationContext),在此类static块中获取了默认配置文件 contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } } }
static { try { // 封装配置文件 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); // 加载配置文件 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } }
此方法主要可以看出,可以自定义contextClass通过web.xml参数形式使用,主要就是生成了SpringIOC容器
(2)调用configureAndRefreshWebApplicationContext方法刷新springIOC容器并关联ServletContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // web.xml有默认参数那么设置默认ID String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // 如果没有默认参数使用默认规则创建ID wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } // 设置ServletContext容器 wac.setServletContext(sc); // 设置获取Spring配置文件地址并设置到容器 String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } customizeContext(sc, wac); // Spring刷新流程 wac.refresh(); }
(3)将IOC容器放入servlet容器中
(4)映射类加载器和实例到全局变量至currentContextPerThread中
4.总结
ContextLoaderListener的作用:初始化spring容器,将web与spring容器相关联