1. 程式人生 > >spring裏頭各種獲取ApplicationContext的方法

spring裏頭各種獲取ApplicationContext的方法

角色 接口 uri 本地應用 創建 lis 問題 重新 import

為啥寫這個文章呢?spring各個版本不同,以及和系統框架套在一起不同,導致獲取的方式不同,網絡上各種版本,太亂了,寫獲取方式的人都不寫這個獲取方式是在本地還是在WEB,在那種應用服務器下,在spring那個版本下,太過分了!


我這寫一些,常見的,可能經常要用的版本;

首先了解,為什麽要獲取這個東西:當你想通過spring獲取一個你指定的類的實例的時候,而又沒有通過spring加載到當前調用的類裏面,例如你在filter裏面,可能要對人員角色做判定,此時還沒到業務層代碼,但是又要訪問數據庫或其他的服務類。

然後再確保一點:這個context是一個全局變量,spring加載的時候,根handle信息就被裝載,無論是本地應用程序還是web應用都是這樣,下面分別說下如果是本地程序和其他情況的獲取方式。

如果是main方法,你要啟動spring,有很多方法,有基於annotation的註解來講配置文件裝載起來,當然,你想獲取applicationCntext可在main方法中這樣獲取:

 XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));//這樣來加載配置文件

還有沒有其他的方式呢?有的

ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"a.xml", "b.xml"});
還有沒有其他的?有
  1. XmlWebApplicationContext context = new XmlWebApplicationContext();
  2. context.setConfigLocations(new String[] {"aaa.xml" , "bb.xml"});
  3. MockServletContext msc = new MockServletContext();
  4. context.setServletContext(msc);
  5. context.refresh();
  6. msc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);

其實方法差不多,他們有著繼承關系,所以方法很多,你每次new的時候,相當於重新創建一個applicationContext,他會重新裝載,所以不適合反復調用,如果自己new,你就應當把它放到一個全局變量中,用main啟動的,當然你通過直接或間接的static應用到這個application即可。

而在WEB上呢,有一種是通過spring來加載spring本身的方式是:
通過實現接口:
org.springframework.context.ApplicationContextAware
然後spring反射,來源文章:http://blog.163.com/xuyang1974@126/blog/static/2684016320101028101923914/
這種方式適在spring 2、3當中均有效:
編寫類:
  1. import org.springframework.beans.BeansException;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.ApplicationContextAware;
  4. import org.springframework.stereotype.Service;
  5. @Service
  6. public class SpringContextHolder implements ApplicationContextAware {
  7. private static ApplicationContext applicationContext;
  8. @Override
  9. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  10. SpringContextHolder.applicationContext = applicationContext;
  11. }
  12. public static ApplicationContext getApplicationContext() {
  13. return applicationContext;
  14. }
  15. public static Object getBean(String beanName) {
  16. return applicationContext.getBean(beanName);
  17. }
  18. public static <T>T getBean(String beanName , Class<T>clazz) {
  19. return applicationContext.getBean(beanName , clazz);
  20. }
  21. }


我這裏是通過annotation註解的,如果不是annotation,那麽可以通過配置文件:
<bean class="xxx.xxx.xxx.SpringContextHolder"></bean>
來進行註入操作,結果一樣,如果的spring配置中,沒有設置byName的話,bean的配置裏面記得要加參數來設置applicationContext來反射進去。
而你要加載spring,很多時候,並不是進入業務層的,因為反射是反射到業務層的,你還沒有進入業務層,怎麽來獲取這個反射的東西呢?除非你反射的時候,用static變量來獲取,那麽就沒有問題了;所以上面的例子中他也用的是static;

當你不想用static來反射,而經常想要用到它的時候,就有很多種獲取方式了。
spring 3以前的版本,我們在WEB應用中通常是這樣獲取的:
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(context);
而contexnt是什麽呢?如果是servlet中,是可以直接通過getServletContext()獲取,
而通過request要這樣獲取:
對於所有的tomcat通用的寫法是:
ServletContext context = req.getSession().getServletContext();
對於tomcat 7以上的寫法是(也就是tomcat 7可以直接從request中獲取servletContext,tomcat6不行,必須通過session才可以):
ServletContext context = req.getServletContext();

其實從spring 3過後,獲取的方法就有所改變,變得很詭異,因為竟然不兼容以前的獲取方法,spring 3當中將其進行了進一步的包裝,你在其他地方可能看到各種各樣的版本。


spring 2中之所以可以那樣獲取,是因為spring 2當中通常會配置一個listener,由他來加載spring,他在filter之前;spring 3當中,通過org.springframework.web.servlet.DispatcherServlet來裝載spring的信息,初始化在其父親類:org.springframework.web.servlet.FrameworkServlet中方法:initWebApplicationContext();

跟蹤方法明顯看到內部獲取增加了一個參數:

WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(),attrName);


這個參數是什麽呢?

經過跟蹤可以發現是:

FrameworkServlet.SERVLET_CONTEXT_PREFIX + getServletName()

SERVLET_CONTEXT_PREFIX的定義是:

public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";

也就是:

“org.springframework.web.servlet.FrameworkServlet.CONTEXT.”

getServletName()呢?他是當前請求的servlet,可以獲取到的一個web.xml裏面配置的名稱,例如,

如果你的web.xml中配置的是:

  1. <servlet>
  2. <servlet-name>spring</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <load-on-startup>1</load-on-startup>
  5. </servlet>



說明getServletName()的結果就是spring,否則就是其他,那麽如果是spring,就是:

org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring

ok,如果按照上面的配置,獲取方式就是:

request.getSession().getServletContext().getAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring");

tomcat 7以上可以寫成:

request.getServletContext().getAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring");


更為好的寫法是:

request.getSession().getServletContext().getAttribute(FrameworkServlet.SERVLET_CONTEXT_PREFIX +"spring");


以下為spring為了方便,做的一些擴展:

spring為了業務代碼中獲取這個參數方便,在進入業務代碼前做了一個操作,在DispatcherServlet的方法:doService中doDispatch調用之前:

request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());


也就是,當你進入Controller以後,獲取就不用那麽麻煩了,你只需要這樣就能獲取到:

request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);

當然,你可以將值寫進去,看定義是:

public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";

那麽值就應該是:

org.springframework.web.servlet.DispatcherServlet.CONTEXT

所以在Controller中你還可以這樣來獲取:

request.getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT")

經過spring包裝後,你也可以通過:

RequestContextUtils.getWebApplicationContext(request , context)

來獲取,源碼如下:

技術分享圖片

其實它獲取的方式和上面給的方法是一樣的,RequestContextUtils.getWebApplicationContext在spring 3當中,如果沒有啟動ContextLoaderListener(當然你可以配置監聽),是不會成功的。


ContextLoaderListener的簡單配置為(web.xml中):

  1. <listener>
  2. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  3. </listener>


spring 3以後基本不這樣配置了。


spring裏頭各種獲取ApplicationContext的方法