WebApplicationContext的三種初始化方式

來源:互聯網
上載者:User

標籤:eth   event   abstract   設定檔   param   proc   就是   兩種   有用   

  

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

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的三種初始化方式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.