ContextLoaderListener - 运行原理
基本概念:
servletContext:http://blog.csdn.net/yjw757174266/article/details/45072975
1、 使用ContextLoaderListener监听器的意义:
a) 为了在servlet容器启动时,就加载applicationContext.xml(spring核心配置文件)配置文件,实例化其中配置的bean类。
b)避免在使用spring容器获取bean时,重复加载配置文件。
2、 spring版本:4.2.4
3、 配置方式:
a)在web.xml文件中编写监听器,并配置全局的上下文变量,提供spring核心配置文件,代码如下:
|
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> |
b) 如果配置了监听器,那么获取bean的方式就要相应的改变一下,代码如下:
|
1、ServletContext servletContext = ServletActionContext.getServletContext(); 2、WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext); 3、XXXService cs = (XXXService) context.getBean("XXXService"); 4、cs.save(); |
c)我们先不管为什么WebApplicationContext对象与ApplicationContext对象一样能获取bean对象,我们此处探究的是,为什么配置了监听器,我们就能从servletcontext中取得WebApplicationContext对象。
4、 源码分析:
a)首先进入org.springframework.web.context.ContextLoaderListener 源码:代码如下:
|
//: org.springframework.web.context.ContextLoaderListener.java package org.springframework.web.context; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } } |
b)我们可以看到此处继承了 ServletContextListener 接口,实现两个方法(contextInitialized、contextDestroyed),在servlet容器启动和销毁的时候做一些事情。
c) 在ContextLoaderListener监听器这个场景下,contextInitialized方法中会调用ContextLoader 类中的initWebApplicationContext方法,实例化出一个WebApplicationContext对象,此方法接收一个servletContext对象,此对象由Servlet容器传入。
5、 然后我们转到initWebApplicationContext方法中:代码如下:
|
//: org.springframework.web.context.ContextLoader.java 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) { 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); } configureAndRefreshWebApplicationContext(cwac, servletContext); } } 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; } } |
a) this.context = createWebApplicationContext(servletContext);首先我们看这句代码,同样我们先不谈就它是怎么创建的,我们见名知意,就是根据servletContext参数,创建一个WebApplicationContext对象,并赋值给ContextLoader类中的内部变量 context。
b) servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 紧接着将已经赋值的context变量,通过一个的标识名称放入servletContext对象中。
c) 看到这里,我想大家都知道为什么可以从ServletContex中取出WebApplicationContext对象了吧?
6、 接着我们分析在客户端获取WebApplicationContext对象的代码,再copy一份到这里:
|
1、ServletContext servletContext = ServletActionContext.getServletContext(); 2、WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext); 3、XXXService cs = (XXXService) context.getBean("XXXService"); 4、cs.save(); |
a) 首先,我们从struts2框架提供的类中,获取servletContext对象。
b) 之后通过WebApplicationContextUtil类中的getWebApplicationContext方法,从servletContext对象中,将WebApplicationContext对象取出:我们进入方法:
|
public static WebApplicationContext getWebApplicationContext(ServletContext sc) { return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); // 此处标识与存入时的一致 } |
c) 进而转入如下方法:
|
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { Assert.notNull(sc, "ServletContext must not be null"); Object attr = sc.getAttribute(attrName); if (attr == null) { return null; } if (attr instanceof RuntimeException) { throw (RuntimeException) attr; } if (attr instanceof Error) { throw (Error) attr; } if (attr instanceof Exception) { throw new IllegalStateException((Exception) attr); } if (!(attr instanceof WebApplicationContext)) { throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr); } return (WebApplicationContext) attr; } |
d) 我们根据上面的两句代码: Object attr = sc.getAttribute(attrName); / return (WebApplicationContext) attr; 不难看出,将WebApplicationContext对象,从servletcontext对象中取出,强制转换并返回。
7、至此
a) 我们就取得了WebApplicationContext对象,并可以使用该对象来获取bean。而WebApplicationContext对象存放在servletcontext对象中,而servletcontext对象会一直存在于内存中,除非关闭servlet容器。这样就保证了只存在一份WebApplicationContext对象在内存中,同时也就保证了spring核心配置文件只会被加载一次。
ContextLoaderListener - 运行原理的更多相关文章
- iis6.0与asp.net的运行原理
这几天上网翻阅了不少前辈们的关于iis和asp.net运行原理的博客,学的有点零零散散,花了好长时间做了一个小结(虽然文字不多,但也花了不少时间呢),鄙人不才,难免有理解不道的地方,还望前辈们不吝赐教 ...
- ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...
- ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行
ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行 核心框架 ASP.NET Core APP 创建与运行 总结 之前两篇文章简析.NET Core 以及与 .NET Framew ...
- 场景9 深入RAC运行原理
场景9 深入RAC运行原理 OPS(Oracle Parallel Server)通过磁盘的节点判定数据是否最新 —> Data Guard —> RAC(Real Ap ...
- Camel运行原理分析
Camel运行原理分析 以一个简单的例子说明一下camel的运行原理,例子本身很简单,目的就是将一个目录下的文件搬运到另一个文件夹,处理器只是将文件(限于文本文件)的内容打印到控制台,首先代码如下: ...
- Web程序的运行原理及流程(一)
自己做Web程序的开发也有两年多了 从最开始跟风学框架 到第一用上框架的欣喜若狂 我相信每个程序员都是这样过来的 在大学学习一门语言 学会后往往很想做一个实际的项目出来 我当时第一次做WEB项目看 ...
- Asp.net WebPages框架运行原理浅析(转)
在Asp.net4和4.5中,新增了WebPages Framework,编写页面代码使用了新的Razor语法,代码更加的简洁和符合Web标准,编写方式更接近于PHP和以前的Asp,和使用 WebFo ...
- ASP.NT运行原理和页面生命周期详解及其应用
ASP.NT运行原理和页面生命周期详解及其应用 1. 下面是我画的一张关于asp.net运行原理和页面生命周期的一张详解图.如果你对具体不太了解,请参照博客园其他帖子.在这里我主要讲解它的实际应用. ...
- jsp学习--JSP运行原理,九大隐式对象和JSP常用标签
一.JSP运行原理 每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理.JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet ...
随机推荐
- Python3常用学习网站总结(随时更新)
Python资源大全 http://python.jobbole.com/84464/ https://github.com/jobbole/awesome-python-cn scrapy: h ...
- asp.net中配置使用Sqlite轻型数据库
Sqlite 管理工具 SQLiteDeveloper及破解 功能特点 表结构设计,数据维护,ddl生成,加密数据库支持,sqlite2,3支持 唯一缺憾,收费,有试用期 破解方法: 注册表删除 HK ...
- WPF:获取DataGrid控件单元格DataGridCell
转载:http://blog.csdn.net/jhqin/article/details/7645357 /* ------------------------------------------- ...
- riot.js教程【一】简介
Riotjs简介 Riotjs是一款简单的.优雅的.组件化UI前端开发框架: 他支持自定义标签(custom tags),拥有令人愉悦的语法,优雅的API和非常小的体积: 为什么需要一个新的界面库 前 ...
- IOS应用FFMPEG库
1.引用资源 build-ffmpeg ffmpeg库生成 -sh开源地址: https://gist.github.com/m1entus/6983547 iFrameExtractor ffmp ...
- CSS之 border 属性
特性 border-width 不支持百分比 border-color 默认颜色是 color border-color 透明值的作用:可利用增加可点击区域,利用内阴影做边框 border 应用 ...
- 张高兴的 Windows 10 IoT 开发笔记:ToF Sensor VL53L0X
GitHub : https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/VL53L0X
- win10 uwp 修改CalendarDatePicker图标颜色
CalendarDatePicker 是一个好用的东西,但是我发现想要修改他右边的那个图标,显示日历的图标颜色,没有这个选项. 如果不知道我说的是哪个,请看下面的图. 左边颜色变化的就是我们要修改的图 ...
- 记一次vscode升级后,格式化Vue出现的问题
一.VSCode中使用vetur插件格式化vue文件时,stylus代码会自动加上大括号.冒号和分号 本来就是简写比较方便舒服,结果一个格式化回到十年前 解决方案: vscode 文件 ->首 ...
- JS中replace()用法举例
语法: string.replace(regexp,replacement) 参数: regexp:声明了要替换的模式的RegExp对象.如果该参数是一个字符串,则将它作为要检索的直接量文本模式,而不 ...