web項目中能夠集成spring的ApplicationContext進行bean的管理,這樣使用起來bean更加便捷,可以利用到不少spring的特性。咱們比較經常使用的web容器有jetty,tomcat,jboss等,以jetty爲例,咱們看一下web容器是如何初始化和啓動spring的context。web
1、Spring容器的加載spring
在web工程中都有一個web.xml文件,jetty在啓動的時候會加載這個配置文件,而且對文件中的各個listener進行加載。ContextLoaderListener繼承了ServletContextListener,ServletContextListener做爲ServletContext的監聽者,會在ServletContext建立、銷燬等過程當中監聽ServletContextEvent事件,而後進行相應處理。關於這一塊能夠參考Spring的事件發佈和監聽機制:(Spring源碼中的ApplicationContext的加強功能)中關於ApplicationContext做爲事件發佈者部分。全部的擴展點都在接受到ServletContextEvent事件時,具體的ContextLoaderListener處理ServletContextEvent代碼以下:tomcat
/** * Initialize the root web application context. */ public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); this.contextLoader.initWebApplicationContext(event.getServletContext()); }
這裏建立了一個contextLoader對象,ContextLoader顧名思義就是context的加載器,由它來完成context的加載:app
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) throws IllegalStateException, BeansException { 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!"); } servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { // Determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. this.context = createWebApplicationContext(servletContext, parent); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), 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; } }
初始化web的context作了兩件事情:1.查看是否指定了父容器,若是存在父容器則獲取父容器;2.建立webContext,並指定父容器。laputa也使用了父子容器的指派特性,二方庫plouto中負責bean加載的context做爲父容器,laputa應用本身做爲子容器,這樣laputa就可以使用到了plouto中聲明的bean(如在plouto中聲明的widgetagContext,在laputa中的tagList能夠順利完成依賴注入)。以前作一個需求時,爲了在tag中暴露cmsTemplateService給組件接入,把cmsTemplateService聲明放到了plouto中,這樣在laputa中可以更加方便引用。建立的webContext,默認給出的是XmlWebApplicationContext,關於這個類你們確定不會陌生,學習ApplicationContext的例子中會常常使用這個容器來加載xml形式的bean配置。到此,咱們得到了一個ApplicationContext,經過這個context咱們能夠獲取當前容器的bean以及父容器的bean。ide
2、如何在應用中使用contextpost
上述獲取context後進行context存放的代碼中有一段很是重要:學習
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
這兩行代碼告知了咱們如何去獲取context:1.從servletContext中去拿;2.從當前的線程Map中去拿。ui
A.servletContext中獲取spring上下文。Spring對這一種獲取方式作了封裝:WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc)方法來獲得WebApplicationContext:this
public static WebApplicationContext getWebApplicationContext(ServletContext sc) { return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); } ———————————————————————————————————————————————————————————————————————————————————————————————————————————— 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) { IllegalStateException ex = new IllegalStateException(); ex.initCause((Exception) attr); throw ex; } if (!(attr instanceof WebApplicationContext)) { throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr); } return (WebApplicationContext) attr; }
經過servletContext獲取上下文,不足之處在於開發者必須可以先得到servletContext做爲入參傳入,因此使用起來不是很方便。spa
從線程Map中獲取spring上下文。在ContextLoader中有靜態方法來獲取spring上下文:
public static WebApplicationContext getCurrentWebApplicationContext() { return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader()); }
這種方法類比與上述方式更加便捷,再也不須要感知到servletcontext的存在。
spring獲取上下文方式------實現ApplicationContextAware接口。這種方式最通用,不單單侷限於web應用。咱們僅須要在用到的類中,讓其繼承ApplicationContextAwar接口,並實現set方法,就可以讓容器在啓動的時候把上下文注入到當前對象中。舉例如流程引擎中的「開始節點」,獲取spring容器上下文並存入PE的context中,方便後續節點可以使用spring容器:
public class InitParamNode implements ApplicationContextAware{ private static final String PARAM_PLUGS = "param.plugs"; private static final String PARAM_EXTENDS = "param.extends"; private static final String PARAM_SIGNS = "param.signs"; private static final String SPRING_CONTEXT = "springContext"; private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } public Map<String,Object> execute(PostParam postParam){ Map<String,Object> context=new HashMap<String,Object>(); context.put(SPRING_CONTEXT, this.applicationContext); if(postParam.getCommodityExtParam()!=null){ context.put(PARAM_SIGNS, postParam.getCommodityExtParam().getSignParam()); context.put(PARAM_EXTENDS, postParam.getCommodityExtParam().getExtendParam()); context.put(PARAM_PLUGS, postParam.getCommodityExtParam().getPluginParam()); } return context; } }
3、