对于SpringMVC功能实现的分析,我们首先从web.xml开始,在web.xml文件中我们首先配置的就是ContextLoaderListener,那么它所提供了功能有哪些又是如何实现的?当使用编程方式的时候我们可以将spring配置传入到Spring容器中,如:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

但是在web下,我们需要更多的是与Web环境相互结合,通常的办法是将路径以context-param的方式注册并使用ContextLoaderListener进行监听读取。

ContextLoaderListener的作用是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法,使用ServletContextListener接口,开发者能够在为客户端请求提供服务之前向ServletContext中添加任意的对象。这个对象在ServletContext启动的时候被初始化,然后在ServletContext整个运行期间都是可见的。每一个web应用都有一个ServletContext与之相关联。ServletContext对象在应用启动时被创建,在应用关闭的时候被销毁。ServletContext在全局范围内有效,类似于应用中的一个全局变量。

在ServletContextListener中的核心逻辑便是初始化WebApplicationContext实例并存在至ServletContext中。

ServletContextListener的使用

先了解下ServletContextListener的使用:

  1. 创建自定义ServletContextListener
  2. 注册监听器测试
  3. 测试
//首先我们创建ServletContextListener,目标是在系统启动时添加自定义属性,
//以便于在全局范围内可以随时调用。系统启动的时候会调用ServletContextListener实现类的contextInitialized方法,
//所以需要在这个方法中实现我们初始化的逻辑。
public class MyDataContextListener implements ServletContextListener{
private ServletContext context = null;
public MyDataContextListener(){
}
//该方法在ServletContext启动之后调用,并准备好处理客户端请求
public void contextInitialized(ServletContextEvent event){
this.context = event.getServletContext();
//通过你可以实现自己的记录并记录在属性中
context = setAttribute("myData","this is myData");
}
//这个方法在ServletContext将要关闭的时候调用
public void contextDestroyed(ServletContextEvent event){
this.context = null;
}
}
//在web.xml中需要注册自定义的监听器
<listener>com.test.MydataContextListener</listener>
//一旦在web应用启动的时候,我们就能在任意的Servlet或者JSP中通过下面的方式获取我们初始化的参数,如下:
//String myData = (String)getServletContext().getAttribute("myData");

Spring中的ContextLoaderListener

ServletContext启动之后会调用ServletContextListener的contextInitialized方法,那么就我们从这个函数开始分析:

public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
//初始化WebApplicationContext
this.contextLoader.initWebApplicationContext(event.getServletContext());
}

这里涉及到了一个常用类WebApplicationContext。在web应用中,我们会用到WebApplicationContext,WebApplicationContext继承自ApplicationContext,在ApplicationContext的基础上又追加了一些特定于web的操作及属性,非常类似使用Spring时使用的ClassPathXmlApplicationContext类提供的功能。继续跟踪代码:

//initWebApplicationContext函数主要是体现了创建WebApplicationContext实例的一个功能架构,从函数中我们看到了初始化的大致步骤
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//WebApplicationContext存在性的验证。
//在配置中值允许声明一次ServletContextListener,多次声明会扰乱Spring的执行逻辑,所以这里首先做的就是对此验证
//在Spring中如果创建WebApplicationContext实例会记录在ServletContext中以方便全局调用,而使用的key就是WebApplicationContext.
//ROOT_WEB_APPLICSTION_CONTEXT_ATTRIBUTE,所以验证方式就是查看ServletContext实例中是否有对应的key的属性。
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 {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
//创建WebApplicationContext实例。
this.context = createWebApplicationContext(servletContext, parent);
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;
}
}

上面这段代码最重要的就是createWebApplicationContext

protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {  
  //获取servlet的初始化参数contextClass,如果没有配置默认为XmlWebApplicationContext.class
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
  //通过反射方式实例化contextClass
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// Assign the best possible id value.
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
String servletContextName = sc.getServletContextName();
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(servletContextName));
}
else {
// Servlet 2.5's getContextPath available!
try {
String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(contextPath));
}
catch (Exception ex) {
throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
}
}
 //parent为在ContextLoaderListener中创建的实例
  //在ContextLoaderListener加载的时候初始化的WebApplicationContext类型实例
wac.setParent(parent);
wac.setServletContext(sc);
  //获取contextConfigLocation属性,配置在servlet初始化参数中
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(sc, wac);
wac.refresh();
return wac;
} //其中,在ContextLoader类中有这样的静态代码块
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());
}
}

根据以上静态代码块我们推断在当前ContextLoader类同目录下必然存在属性文件ContextLoader.properties,看后果然存在,代码如下:org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

    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);
}
}
}

总结:

WebApplicationContext存在性的验证。在配置中只允许声明一次ServletContextListener,多次声明会扰乱Spring的执行逻辑,所以这里首先做的就是对此验证,在Spring中如果创建WebApplicationContext实例会记录在ServletContext中以方便全局调用,而使用的key就是WebApplicationContext.ROOT_WEB_ APPLICATION_CONTEXT_ATTRIBUTE,所以验证的方式就是查看ServletContext实例中是否有对应key的属性。

创建WebApplicationContext实例。如果通过验证,则Spring将创建WebApplicationContext实例的工作委托给了create WebApplicationContext函数。

在初始化的过程中,程序首先会读取ContextLoader类同目录下的属性文件ContextLoader.properties。并根据其中配置提取将要实现WebApplicationContext接口的实现类,并根据这个实现类通过反射的方式进行实例的创建

将实例记录在servletContext中customizeContext(sc, wac);

映射当前的类加载器与创建的实例到全局变量currentContextPerhread中。

SpringMVC解析2-ContextLoaderListener的更多相关文章

  1. springmvc 解析xml数据

    springmvc 解析xml数据 http://blog.csdn.net/zhi_jun/article/details/37925475

  2. SpringMVC解析3-DispatcherServlet组件初始化

    在spring中,ContextLoaderListener只是辅助功能,用于创建WebApplicationContext类型实例,而真正的逻辑实现其实是在DispatcherServlet中进行的 ...

  3. SpringMVC解析1-使用示例

    Spring MVC分离了控制器.模型对象.分派器以及处理程序对象的角色,这种分离让它们更容易进行定制.Spring的MVC是基于servlet功能实现的,通过实现Servlet接口的Dispatch ...

  4. SpringMVC解析5-DispatcherServlet逻辑细节

    MultipartContent类型的request处理 对于请求的处理,spring首先考虑的是对于Multipart的处理,如果是MultipartContent类型的request,则转换req ...

  5. SpringMVC 解析(一)概览

    Spring MVC是Spring提供的构建Web应用程序的框架,该框架遵循了Servlet规范,负责接收并处理Servelt容器传递的请求,并将响应写回Response.Spring MVC以Dis ...

  6. SpringMVC解析Json字符串

    不同第三方jar对json串的解析效果不同. 1. json包 <dependency> <groupId>org.json</groupId> <artif ...

  7. 微信推送给服务器的XML消息解析-springmvc 解析xml数据流

    微信推送给服务器的XML消息解析: 可以使用request.getInputStream(); 获取输入的消息流:但是需要自己解析流: spring mvc自带解析功能: controller中: @ ...

  8. SpringMVC 解析(二)DispatcherServlet

    在我的关于Tomcat容器介绍的文章中,介绍了Tomcat容器的工作原理,我们知道Tomcat容器在收到请求之后,会把请求处理为Request/Response对象,交给Servlet实例处理.对于S ...

  9. SpringMVC 解析(三) Controller 注解

    我在前面的文章中介绍了Spring MVC最核心的组件DispatcherServlet,DispatcherServlet把Servlet容器(如Tomcat)中的请求和Spring中的组件联系到一 ...

随机推荐

  1. nyoj_34_韩信点兵

     中国剩余定理: 代码: #include <iostream> #include <cstdio> using namespace std; int main() { int ...

  2. 关于定时器 setTimeout

    1.这里不考虑线程问题.把javascript想象成在时间线上运行,页面载入时,首先执行的是<script>标签中的代码,之后,将执行更多代码,当进程空闲时,下个代码就被触发并执行 如图: ...

  3. Aix下如何运行Java程序

    windows下:java -classpath %classpath%;bb.jar;aa.jar [main class]main class是打包的主类,已经指定了主类,可以不输入.另外,IBM ...

  4. C#中XmlTextWriter读写xml文件详细介绍(转)

    转自http://www.jb51.net/article/35230.htm   .NET中包含了很多支持XML的类,这些类使得程序员使用XML编程就如同理解XML文件一样简单.在这篇文章中,我将给 ...

  5. ExpandableListView的用法

    ExpandableListView组件是android中一个比较常用的组件,当点击一个父item的时候可以将它的子item显示出来,像手机QQ中的好友列表就是实现的类型效果.使用Expandable ...

  6. 【python】入门学习(四)

    函数: 定义函数 #area.py from math import pi def area(radius): """Return the area of a circl ...

  7. bootstrap清除拟态框内添加新HTML再打开时会有缓存现象

    $(function(){ $("#editor").on("hidden.bs.modal",function(){ //清除缓存方法 $(this).fin ...

  8. 26. Remove Duplicates from Sorted Array

    题目: Given a sorted array, remove the duplicates in place such that each element appear only once and ...

  9. 解决SQL Server的cannot resolve the collation conflict问题

    当没有牵涉到两个不同的数据库时,出现以上错误.   Cannot resolve the collation conflict between "Chinese_PRC_CI_AS" ...

  10. [Android Pro] Gradle tip #3-Task顺序

    reference to : http://blog.csdn.net/lzyzsd/article/details/46935405 原文链接 我注意到我在使用Gradle的时候遇到的大多数问题都是 ...