上一篇我们简单说了在初始化DiapatcherServlet的时候,他是如何来创建ioc容器并去加载bean的,但是我们还有aplicationContext.xml文件没有被加载进去。同理,我们的mybatis-config.xml也没有被加载
那么这一篇我们来分析下如何才能让我们的applicationContext.xml文件被加载
基于监听器的xml文件的加载
在servlet中,我们往往会在web.xml文件中来配置一个监听器,在我们web服务启动之后用它去处理一些事情。
web.xml中配置监听器
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
来看一下ContextLoaderListener的定义信息
通过以下代码可以知道,他就是一个servlet的监听器,那么在web服务启动之后,必然会执行他的contextInitialized方法,在web服务关闭之前也会执行contextDestroyed方法
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
......
public void contextInitialized(ServletContextEvent event) {
//初始化WebApplicationContext
this.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
初始化WebApplicationContext
ContextLoader 73 line
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!");
} else {
try {
if (this.context == null) {
//创建ioc容器 这一块和上一篇的创建一样
this.context = this.createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = this.loadParentContext(servletContext);
cwac.setParent(parent);
}
//初始化ioc容器
this.configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//保存起来
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
......
}
......
}
}
初始化ioc容器
ContextLoader 160 line
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
String configLocationParam;
//设置下容器id org.springframework.web.context.WebApplicationContext:
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
configLocationParam = sc.getInitParameter("contextId");
if (configLocationParam != null) {
wac.setId(configLocationParam);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
//获取配置文件的路径 这个也就是我们在web.xml里面配置的
configLocationParam = sc.getInitParameter("contextConfigLocation");
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
}
this.customizeContext(sc, wac);
wac.refresh();
}
到了这里 ,有灵性的人应该可以知道,上一篇说过了默认的springmvc.xml的加载位置以及命名的规则,同样这里综合前面的也就可以知道,从监听器这里进来加载xml文件的时候,默认的取的就是
/WEB-INF/applicationContext.xml
因为这里namespace肯定为null,因为我们从没有设置过这个东西,而上一篇在加载springmvc.xml的时候有一行代码是设置了这个的 代码如下
FrameworkServlet 284 line
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
......
//dispatcherServlet-servlet 这个就是默认springmvc.xml的名字
wac.setNamespace(this.getNamespace());
......
}
这里贴出完整的web.xml吧。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<!--1、启动Spring的容器 -->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--2、springmvc的前端控制器,拦截所有请求 -->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 3、字符编码过滤器,一定要放在所有过滤器之前 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 4、使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>HttpPutFormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>