【Spring MVC学习】WebApplicationContext初始化的三种方式

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.
  • 1
  • 2
  • 3

这两个方法都是在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> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

那么在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());  
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

显然,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;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

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

第一种获取方式:

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

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

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

确实可以,而且我们想到这种方法的时候,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;  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

getWebApplicationContext方法如下:

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {  
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  
    }  
  • 1
  • 2
  • 3
  • 4

第二种方法:

前面说到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());  
    }  
  • 1
  • 2
  • 3
  • 4

第二种方法与第一种方法相比有什么好的地方呢?就是它不需要参数,只要在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;  

}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

就这一个接口。可以这样简单的实现一个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;  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值