web项目中可以集成spring的ApplicationContext进行bean的管理,这样使用起来bean更加便捷,能够利用到很多spring的特性。我们比较常用的web容器有jetty,tomcat,jboss等,以jetty为例,我们看一下web容器是如何初始化和启动spring的context。

一、Spring容器的加载

  在web工程中都有一个web.xml文件,jetty在启动的时候会加载这个配置文件,并且对文件中的各个listener进行加载。ContextLoaderListener继承了ServletContextListener,ServletContextListener作为ServletContext的监听者,会在ServletContext创建、销毁等过程中监听ServletContextEvent事件,然后进行相应处理。关于这一块可以参考Spring的事件发布和监听机制:(Spring源码中的ApplicationContext的增强功能)中关于ApplicationContext作为事件发布者部分。所有的扩展点都在接受到ServletContextEvent事件时,具体的ContextLoaderListener处理ServletContextEvent代码如下:

  1. /**
  2. * Initialize the root web application context.
  3. */
  4. public void contextInitialized(ServletContextEvent event) {
  5. this.contextLoader = createContextLoader();
  6. this.contextLoader.initWebApplicationContext(event.getServletContext());
  7. }

这里创建了一个contextLoader对象,ContextLoader顾名思义就是context的加载器,由它来完成context的加载:

  1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
  2. throws IllegalStateException, BeansException {
  3.  
  4. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
  5. throw new IllegalStateException(
  6. "Cannot initialize context because there is already a root application context present - " +
  7. "check whether you have multiple ContextLoader* definitions in your web.xml!");
  8. }
  9.  
  10. servletContext.log("Initializing Spring root WebApplicationContext");
  11. if (logger.isInfoEnabled()) {
  12. logger.info("Root WebApplicationContext: initialization started");
  13. }
  14. long startTime = System.currentTimeMillis();
  15.  
  16. try {
  17. // Determine parent for root web application context, if any.
  18. ApplicationContext parent = loadParentContext(servletContext);
  19.  
  20. // Store context in local instance variable, to guarantee that
  21. // it is available on ServletContext shutdown.
  22. this.context = createWebApplicationContext(servletContext, parent);
  23. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  24. currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
  25.  
  26. if (logger.isDebugEnabled()) {
  27. logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
  28. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
  29. }
  30. if (logger.isInfoEnabled()) {
  31. long elapsedTime = System.currentTimeMillis() - startTime;
  32. logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
  33. }
  34.  
  35. return this.context;
  36. }
  37. catch (RuntimeException ex) {
  38. logger.error("Context initialization failed", ex);
  39. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  40. throw ex;
  41. }
  42. catch (Error err) {
  43. logger.error("Context initialization failed", err);
  44. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
  45. throw err;
  46. }
  47. }

初始化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。

二、如何在应用中使用context

上述获取context后进行context存放的代码中有一段非常重要:

  1. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  2. currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);

  这两行代码告知了我们如何去获取context:1.从servletContext中去拿;2.从当前的线程Map中去拿。

  A.servletContext中获取spring上下文。Spring对这一种获取方式做了封装:WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc)方法来得到WebApplicationContext:

  1. public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
  2. return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
  3. }
  4. ————————————————————————————————————————————————————————————————————————————————————————————————————————————
  5. public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
  6. Assert.notNull(sc, "ServletContext must not be null");
  7. Object attr = sc.getAttribute(attrName);
  8. if (attr == null) {
  9. return null;
  10. }
  11. if (attr instanceof RuntimeException) {
  12. throw (RuntimeException) attr;
  13. }
  14. if (attr instanceof Error) {
  15. throw (Error) attr;
  16. }
  17. if (attr instanceof Exception) {
  18. IllegalStateException ex = new IllegalStateException();
  19. ex.initCause((Exception) attr);
  20. throw ex;
  21. }
  22. if (!(attr instanceof WebApplicationContext)) {
  23. throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
  24. }
  25. return (WebApplicationContext) attr;
  26. }

通过servletContext获取上下文,不足之处在于开发者必须能够先获得servletContext作为入参传入,所以使用起来不是很方便。

从线程Map中获取spring上下文。在ContextLoader中有静态方法来获取spring上下文:

  1. public static WebApplicationContext getCurrentWebApplicationContext() {
  2. return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());
  3. }

这种方法类比与上述方式更加便捷,不再需要感知到servletcontext的存在。

spring获取上下文方式------实现ApplicationContextAware接口。这种方式最通用,不仅仅局限于web应用。我们仅需要在用到的类中,让其继承ApplicationContextAwar接口,并实现set方法,就能够让容器在启动的时候把上下文注入到当前对象中。举例如流程引擎中的“开始节点”,获取spring容器上下文并存入PE的context中,方便后续节点能够使用spring容器:

  1. public class InitParamNode implements  ApplicationContextAware{
  2.     private static final String PARAM_PLUGS = "param.plugs";
  3.     private static final String PARAM_EXTENDS = "param.extends";
  4.     private static final String PARAM_SIGNS = "param.signs";
  5.     private static final String SPRING_CONTEXT = "springContext";
  6.     private ApplicationContext applicationContext;
  7.  
  8.     @Override
  9.     public void setApplicationContext(ApplicationContext applicationContext)
  10.             throws BeansException {
  11.         this.applicationContext=applicationContext;
  12.          
  13.     }
  14.      
  15.     public Map<String,Object> execute(PostParam postParam){
  16.         Map<String,Object> context=new HashMap<String,Object>();
  17.          
  18.         context.put(SPRING_CONTEXT, this.applicationContext);
  19.          
  20.         if(postParam.getCommodityExtParam()!=null){
  21.             context.put(PARAM_SIGNS, postParam.getCommodityExtParam().getSignParam());
  22.             context.put(PARAM_EXTENDS, postParam.getCommodityExtParam().getExtendParam());
  23.             context.put(PARAM_PLUGS, postParam.getCommodityExtParam().getPluginParam());
  24.         }
  25.      
  26.          
  27.          
  28.         return context;
  29.          
  30.     }
  31.  
  32. }

三、

spring源码:web容器启动的更多相关文章

  1. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  2. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  3. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

  4. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  5. Spring源码系列——容器的启动过程(一)

    一. 前言 Spring家族特别庞大,对于开发人员而言,要想全面征服Spring家族,得花费不少的力气.俗话说,打蛇打七寸,那么Spring家族的"七寸"是什么呢?我心目中的答案一 ...

  6. Spring源码系列 — 容器Extend Point(一)

    前言 前文介绍了Spring中的BeanDefinition的细节,随着Spring的启动流程,这节我们介绍Spring的后续处理过程 - Spring的扩展点: BeanFactoryPostPro ...

  7. Spring源码学习-容器BeanFactory(五) Bean的创建-探寻Bean的新生之路

    写在前面 上面四篇文章讲了Spring是如何将配置文件一步一步转化为BeanDefinition的整个流程,下面就到了正式创建Bean对象实例的环节了,我们一起继续学习吧. 2.初始化Bean对象实例 ...

  8. spring源码-增强容器xml解析-3.1

    一.ApplicationContext的xml解析工作是通过ClassPathXmlApplicationContext来实现的,其实看过ClassPathXmlApplicationContext ...

  9. spring源码-ioc容器周期

    Spring容器的refresh 创建刷新:   1-prepareRefresh刷新前的预处理: initPropertySources 初始化一些属性配置,原来是空的,子类自定义的属性设置方法 g ...

  10. spring源码-bean之增强初始化-3

    一.ApplicationContext的中文意思是“应用上下文”,它继承自BeanFactory接口,除了包含BeanFactory的所有功能之外,在国际化支持.资源访问(如URL和文件).事件传播 ...

随机推荐

  1. Redis自学笔记:3.6入门-有序集合类型

    3.6有序集合类型 3.6.1介绍 在集合类型基础上,为集合中每个元素都关联了一个分数,故可以获得 分数最高(最低)的前N个元素,可以获得指定范围内的元素等 有序集合中每个元素不同,但它们的分数却可以 ...

  2. BZOJ.4399.魔法少女LJJ(线段树合并)

    BZOJ 注意\(c\leq7\)→_→ 然后就是裸的权值线段树+线段树合并了. 对于取\(\max/\min\)操作可以直接区间修改清空超出范围的值,然后更新到对应位置上就行了(比如对\(v\)取\ ...

  3. 数据库——MongoDB的安装

    1.进入到 /usr/local/ 目录: 1 cd /usr/local 2.安装必要插件 yum -y install gcc make gcc-c++ openssl-devel wget yu ...

  4. Ubuntu卸载软件

    在终端中输入 sudo dpkg --list 查看已安装的软件,得知需要卸载的软件名为<programme> 再输入 sudo apt-get --purge remove <pr ...

  5. MySQL(五)

    关系 创建成绩表scores,结构如下 id 学生 科目 成绩 思考:学生列应该存什么信息呢? 答:学生列的数据不是在这里新建的,而应该从学生表引用过来,关系也是一条数据:根据范式要求应该存储学生的编 ...

  6. 学习Struts--Chap03:struts.xml常用配置(基础)

    1.package属性 name:包名 用来唯一的指定一个package.package可以扩展,当一个package扩展自 另一个package时该package会在本身配置的基础上加入扩展的pac ...

  7. 正则表达式零宽断言详解(?=,?<=,?!,?<!)

    在使用正则表达式时,有时我们需要捕获的内容前后必须是特定内容,但又不捕获这些特定内容的时候,零宽断言就起到作用了 正则表达式零宽断言: 零宽断言是正则表达式中的难点,所以重点从匹配原理方面进行分析.零 ...

  8. JS_高程5.引用类型(1)Object类型

    引用类型 在ECMASCript中,引用类型是一种数据结构,将数据和功能组织在一起,引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法.(注意:尽管ECMAScript从技术上 ...

  9. 【二分图带权匹配】Anagram @山东省第九届省赛 A

    题目描述 Orz has two strings of the same length: A and B. Now she wants to transform A into an anagram o ...

  10. java调用第三方的webservice应用实例

    互联网上面有很多的免费webService服务,我们可以调用这些免费的WebService服务,将一些其他网站的内容信息集成到我们的Web应用中显示. 一些常用的webservice网站的链接地址: ...