原博客地址:http://blog.csdn.net/lmb55/article/details/50510547

接下来以ContextLoaderListener为例,分析它到底做了什么?

applicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了;在Web应用中,我们会用到WebApplicationContext,WebApplicationContext继承自ApplicationContext;WebApplicationContext的初始化方式和BeanFactory.ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,也就是说它必须拥有Web容器的前提下才能完成启动的工作.有过Web开发经验的读者都知道可以在web.xml中配置自启动的Servlet或定义Web容器监听器(ServletContextListener),借助着两者中的任何一个,我们就可以启动Spring Web应用上下文的工作.

Spring分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器:

org.springframework.web.context.ContextLoaderServlet;

org.springframework.web.context.ContextLoaderListener.

这两个方法都是在web应用启动的时候来初始化WebApplicationContext,我个人认为Listerner要比Servlet更好一些,因为Listerner监听应用的启动和结束,而Servlet得启动要稍微延迟一些,如果在这时要做一些业务的操作,启动的前后顺序是有影响的。

配置例子如下:

context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

那么在ContextLoaderListener和ContextLoaderServlet中到底做了什么呢? 
以ContextLoaderListener为例,我们可以看到

public class ContextLoaderListener implements ServletContextListener {  

    private ContextLoader contextLoader;  

    /**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
}

显然,ContextLoaderListener实现了ServeletContextListenet,在ServletContext初始化的时候,会进行Spring的初始化,大家肯定会想,Spring的初始化应该与ServletContext有一定关系吧?有关系吗?接下来让我们看看 
ContextLoader.initWebApplicationContext方法。

ContextLoader是一个工具类,用来初始化WebApplicationContext,其主要方法就是initWebApplicationContext,我们继续研究initWebApplicationContext这个方法:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)  throws IllegalStateException, BeansException {  

            //从ServletContext中查找,是否存在以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为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!");
} try {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext); // it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
//将ApplicationContext放入ServletContext中,其key为<WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
//将ApplicationContext放入ContextLoader的全局静态常量Map中,其中key为:Thread.currentThread().getContextClassLoader()即当前线程类加载器 currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context); return this.context;
}

从上面的代码大家应该明白了Spring初始化之后,将ApplicationContext存到在了两个地方(servletContext中和currentContextPerThread中),那么是不是意味着我们可以通过两种方式取得ApplicationContext?

第一种获取方式:

 注:WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
即为 "org.springframework.web.context.WebApplicationContext.ROOT"

那么咱们是不是可以这样获得ApplicationContext:

request.getSession().getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT")  

确实可以,而且我们想到这种方法的时候,Spring早就提供给我们接口了:

public abstract class WebApplicationContextUtils {  

public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc)  throws IllegalStateException {  

        WebApplicationContext wac = getWebApplicationContext(sc);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
}
return wac;
}

getWebApplicationContext方法如下:

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

第二种方法:

前面说到Spring初始化的时候,将ApplicationContext还存了一份到ContextLoader的Map里面,那么我们是不是可以通过Map.get(key) ???很不幸的是,这个Map是私有的。

private static final Map currentContextPerThread = CollectionFactory.createConcurrentMapIfPossible(1);
  • 1
  • 2

Spring为我们提供了方法:

public static WebApplicationContext getCurrentWebApplicationContext() {
return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());
}

第二种方法与第一种方法相比有什么好的地方呢?就是它不需要参数,只要在Web容器中,当Spring初始化之后,你不需要传入任何参数,就可以获得ApplicationContext。不过这个方法在Spring2.52版本中是不存在的,但是在2.5.5版本中提供了。

其实第二种获取方法看上去简单,但他的原理还是有一定难度的,他与类加载器的线程上下文相关,这个线程上下文在咱们常用的Mysql驱动中有用到。

第三种方式:

借用ApplicationContextAware,ApplicationContext的帮助类能够自动装载ApplicationContext,只要你将某个类实现这个接口,并将这个实现类在Spring配置文件中进行配置,Spring会自动帮你进行注入 ApplicationContext.ApplicationContextAware的代码结构如下:

public interface ApplicationContextAware {  

        void setApplicationContext(ApplicationContext applicationContext) throws BeansException;  

}

就这一个接口。可以这样简单的实现一个ApplicationContextHelper类:

public class ApplicationHelper implements ApplicationContextAware {  

    private ApplicationContext applicationContext; 

    public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
} public ApplicationContext getApplicationContext(){
return this.applicationContext;
}
}

通过ApplicationHelper我们就可以获得咱们想要的AppilcationContext类了。

WebApplicationContext初始化的两种方式和获取的三种方式的更多相关文章

  1. Java反射02 : Class对象获取的三种方式和通过反射实例化对象的两种方式

    1.Class对象获取的三种方式 本文转载自:https://blog.csdn.net/hanchao5272/article/details/79361463 上一章节已经说过,一般情况下,Jav ...

  2. Struts2获取request三种方法

    Struts2获取request三种方法   struts2里面有三种方法可以获取request,最好使用ServletRequestAware接口通过IOC机制注入Request对象. 在Actio ...

  3. 数据库时间内接受的是lang类型的时间 分为三种字段 第一种只存日期 第二种存日期+时间 第三种时间戳

    数据库时间内接受的是lang类型的时间 分为三种字段 第一种只存日期 第二种存日期+时间 第三种时间戳

  4. Metadata获取的三种方式

    本文的试验环境为CentOS 7.3,Kubernetes集群为1.11.2,安装步骤参见kubeadm安装kubernetes V1.11.1 集群 0. Metadata 每个Pod都有一些信息, ...

  5. C++中实现回调机制的几种方式(一共三种方法,另加三种)

    (1)Callback方式Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型. 比如下面的示例代码,我们在Down ...

  6. 两种设置disabled属性以及三种方法移除disabled属性

    //两种方法设置disabled属性 $('#areaSelect').attr("disabled",true); $('#areaSelect').attr("dis ...

  7. select选中获取索引三种写法

    $('#someId').prop('selectedIndex'); $('option:selected', '#someId').index(); $('#someId option').ind ...

  8. Java反射定义、获取Class三种方法

    反射机制的定义: 在运行状态时(动态的),对于任意一个类,都能够得到这个类的所有属性和方法.  对于任意一个对象,都能够调用它的任意属性和方法. Class类是反射机制的起源,我们得到Class类对象 ...

  9. 两个Map的对比,三种方法,将对比结果写入文件。

    三种方法的思维都是遍历一个map的Key,然后2个Map分别取这2个Key值所得到的Value. #第一种用entry private void compareMap(Map<String, S ...

随机推荐

  1. BZOJ 3132: 上帝造题的七分钟 树状数组+差分

    这个思路很巧妙啊 ~ code: #include <cstdio> #include <algorithm> #define N 2050 #define ll int #d ...

  2. BZOJ 4826: [Hnoi2017]影魔 单调栈+可持久化线段树

    Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样 的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个 ...

  3. Codeforces886(Technocup2018) F Symmetric Projections

    Codeforces886(Technocup2018) F Symmetric Projections You are given a set of n points on the plane. A ...

  4. [RN] React Native 使用 图片预览和放大 插件 react-native-image-zoom-viewer 过程中,放大报错问题

    React Native 使用 图片预览和放大 插件 react-native-image-zoom-viewer 过程中,放大报错问题 报错如下: Cannot record touch end w ...

  5. 深入js系列-类型(隐式强制转换)

    隐式强制转换 在其可控的情况下,减少冗余,让代码更简洁,很多地方都进行了隐式转换,比如常见的三目表达式.if().for().while.逻辑运算符 || &&,适当通过语言机制,抽象 ...

  6. 微信网页分享使用了jssdk,分享图还是不显示的几个坑

    坑爹的微信分享,设置图片链接必须要满足如下条件: 1. 微信分享图链接必须是绝对路径,写相对地址不行. 比如图片地址写成 './assets/images/share.jpg' 不行!!! 必须写成 ...

  7. 常见网页编辑器(富文本,Markdown,代码编辑等)

    编辑器:网页不常用的功能,但却又是不可少的功能,如果要造个编辑器轮子,它可以把人玩死!!前端几大禁忌就有富文本, 为什么都说富文本编辑器是天坑? 下面记录一下常见的一些编辑器,该文随时更新: 富文本编 ...

  8. 图像处理pillow模块

    pillow模块: -->基本的图像处理模块 Pip install pillow from PIL import Image #1.读取图片 im = Image.open('/test.jp ...

  9. 使用dozer将DTO转化为DO

    DTO,就是Data Transfer Object,数据传输对象,可以简单理解成请求中的对象. PO,就是Persistant Object,持久化对象,它跟持久层(通常是关系型数据库)的数据结构形 ...

  10. 微信公众平台开发(150)——从新浪云SAE上传图片到图文消息

    从新浪云SAE上传图片到图文消息,只能用于图文消息中, 没有个数限制 if (!empty($_FILES['qrcode']['name'])){ $filename = time()." ...