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、