收藏私塾在线
 

欢迎您来到私塾在线网!   

请登录! 

免费注册 


zhang的笔记
状态: 离线
人气:5086820
访问用户量:4227
笔记经验:
总积分:261656
级别:VIP5
搜索本笔记
ta的交流分类
ta的交流主题贴(544)
ta的所有交流贴(1049)
ta的全部笔记
全部笔记(255)
未分类笔记(1)
Java Web(9)
并发实践(1)
课程问题(0)
Java(22)
架构(1)
缓存(5)
JavaEE(0)
JVM(12)
跟我学spring3(68)
Spring Sec……(43)
Spring 3.x……(25)
Spring Sec……(20)
跟开涛学Spring……(17)
深入剖析Spring……(18)
性能调优(10)
前端(2)
Tomcat源码解读(1)
spring sec……(0)
存档
2014-01(7)
2013-12(10)
2012-10(4)
2012-09(2)
2012-08(31)
2012-07(10)
2012-06(5)
2012-05(41)
2012-04(3)
2012-03(41)
2012-02(54)
2011-11(17)
2011-10(30)

2012-08-03 09:09:13
深入剖析Spring Web源码(七) - DispatcherServlet的实现 - 根共享环境的加载/其他Servlet
浏览(4826)|评论(0)   交流分类:Java|笔记分类: 深入剖析Spring……

 

1.1.3   根共享环境的加载

 

上一节中我们在分析框架Sevlet是如何初始化Web应用程序环境的时候得知,一个Servlet拥有一个专用的子环境,但是这个子环境可以而且通常引用一个根共享环境,这个根共享环境是通过Servlet环境监听器加载的。也就是说,当一个Servlet环境,也就是一个应用程序被容器加载时,监听器通过监听这个初始化事件初始化根共享Web应用程序,而当一个Servlet环境析构时,监听器功过监听这个析构时间析构共享的Web应用程序环境。

 

下面是整个根共享环境加载的类图,

 

 

 

图表 4‑9

 

我们可以看到,类环境加载监听器类实现了Servlet规范中定义的Servlet环境监听器用以处理初始化事件和析构事件。而真正的根共享环境的创建的实现是环境加载类中实现的。在环境加载类中,通过Servlet初始化参数配置的根共享环境位置加载Web应用程序环境,并且将这个环境以ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为关键字保存在Servlet环境中,这个根共享环境在Servlet加载专用子环境中被引用作为父环境。

 

 

[java]  view plain copy
  1. public void contextInitialized(ServletContextEvent event) {  
  2.     //这个方法实现的本意是提供一个占位符方法createContextLoader()给子类机会创建客户化的环境加载,但是,后来这个证明不是非常有用的,已经鼓励不再使用了,事实上,子类可以通过重写本方法达到同样的目的  
  3.     this.contextLoader = createContextLoader();  
  4.       
  5.     //没有子类实现createContextLoader()占位符 方法,则使用超类的缺省实现,超类 就是环境 加载类  
  6.     if (this.contextLoader == null) {  
  7.         //实际上是为了使用超类的默认实现  
  8.         this.contextLoader = this;  
  9.     }  
  10.       
  11.     //调用超类的加载根共享Web应用程序环境的默认实现  
  12.     this.contextLoader.initWebApplicationContext(event.getServletContext());  
  13. }  
  14.   
  15. public void contextDestroyed(ServletContextEvent event) {  
  16.     //如果环境加载存在,那么关闭环境加载的Web应用程序环境  
  17.     if (this.contextLoader != null) {  
  18.         this.contextLoader.closeWebApplicationContext(event.getServletContext());  
  19.     }  
  20.       
  21.     //清除保存在Servlet环境中的任何可释放的Bean  
  22.     ContextCleanupListener.cleanupAttributes(event.getServletContext());  
  23. }  
  24.   
  25. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
  26.     //如果已经存在了根共享Web应用程序环境,则抛出异常提示客户  
  27.     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
  28.         throw new IllegalStateException(  
  29.                 "Cannot initialize context because there is already a root application context present - " +  
  30.                 "check whether you have multiple ContextLoader* definitions in your web.xml!");  
  31.     }  
  32.   
  33.     Log logger = LogFactory.getLog(ContextLoader.class);  
  34.     servletContext.log("Initializing Spring root WebApplicationContext");  
  35.     if (logger.isInfoEnabled()) {  
  36.         logger.info("Root WebApplicationContext: initialization started");  
  37.     }  
  38.       
  39.     //记录创建根Web应用程序环境的开始时间  
  40.     long startTime = System.currentTimeMillis();  
  41.   
  42.     try {  
  43.         //决定根Web应用程序环境是否存在父应用程序环境  
  44.         ApplicationContext parent = loadParentContext(servletContext);  
  45.   
  46.         //创建根Web应用程序环境,如果父环境存在则引用父环境,通常情况下父环境是不存在的  
  47.         this.context = createWebApplicationContext(servletContext, parent);  
  48.   
  49.         //把创建的根Web应用程序环境保存到Servlet环境中,每个派遣器Servlet加载的子环境会应用这个环境作为父环境  
  50.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
  51.   
  52.   
  53.         //取得线程的类加载器  
  54.         ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
  55.         if (ccl == ContextLoader.class.getClassLoader()) {  
  56.             //如果线程和本类拥有相同的类加载器,则使用静态变量保存即可,因为同一类加载器加载同一份静态变量  
  57.             currentContext = this.context;  
  58.         }  
  59.         else if (ccl != null) {  
  60.             //如果线程和本类拥有不同的类加载器,则使用线程的类加载器作为关键在保存在一个映射对象里,保证析构时能拿到Web应用程序环境进行关闭操作  
  61.             currentContextPerThread.put(ccl, this.context);  
  62.         }  
  63.   
  64.         if (logger.isDebugEnabled()) {  
  65.             logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +  
  66.                     WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");  
  67.         }  
  68.         if (logger.isInfoEnabled()) {  
  69.             long elapsedTime = System.currentTimeMillis() - startTime;  
  70.             logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");  
  71.         }  
  72.   
  73.         return this.context;  
  74.     }  
  75.     catch (RuntimeException ex) {  
  76.         logger.error("Context initialization failed", ex);  
  77.         //如果产生任何异常,则保存异常对象到Servlet环境里  
  78.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
  79.         throw ex;  
  80.     }  
  81.     catch (Error err) {  
  82.         logger.error("Context initialization failed", err);  
  83.         //如果产生任何错误,则保存错误对象到Servlet环境里  
  84.         servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
  85.         throw err;  
  86.     }  
  87. }  
  88.   
  89. protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {  
  90.     //取得配置的Web应用程序环境类,如果没有配置,则使用缺省的类XmlWebApplicationContext  
  91.     Class<?> contextClass = determineContextClass(sc);  
  92.       
  93.     //如果配置的Web应用程序环境类不是可配置的Web应用程序环境的子类,则抛出异常,停止初始化  
  94.     if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
  95.         throw new ApplicationContextException("Custom context class [" + contextClass.getName() +  
  96.                 "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");  
  97.     }  
  98.       
  99.     //否则实例化Web应用程序环境类  
  100.     ConfigurableWebApplicationContext wac =  
  101.             (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
  102.   
  103.     //设置Web应用程序环境的ID  
  104.     if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {  
  105.         // 如果 Servlet规范 <= 2.4,则使用web.xml里定义的应用程序名字定义Web应用程序名  
  106.         String servletContextName = sc.getServletContextName();  
  107.           
  108.         //设置ID  
  109.         wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
  110.                 ObjectUtils.getDisplayString(servletContextName));  
  111.     }  
  112.     else {  
  113.         // 如果Servlet规范是 2.5, 则使用配置的ContextPath定义Web应用程序名  
  114.         try {  
  115.             String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);  
  116.               
  117.             //设置ID  
  118.             wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
  119.                     ObjectUtils.getDisplayString(contextPath));  
  120.         }  
  121.         catch (Exception ex) {  
  122.             //如果Servlet规范是2.5,但是不能取得ContextPath,抛出异常  
  123.             throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);  
  124.         }  
  125.     }  
  126.   
  127.     //如果父环境存在,则引用使用父环境  
  128.     wac.setParent(parent);  
  129.       
  130.     //保存Servlet环境  
  131.     wac.setServletContext(sc);  
  132.       
  133.     //设置环境的位置  
  134.     wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));  
  135.       
  136.     //提供子类可互换Web应用程序环境的机会  
  137.     customizeContext(sc, wac);  
  138.       
  139.     //刷新Web应用程序环境以加载Bean定义  
  140.     wac.refresh();  
  141.     return wac;  
  142. }  
  143.   
  144. protected Class<?> determineContextClass(ServletContext servletContext) {  
  145.     //首先检查是否初始化参数中定义了Web应用程序环境的类名  
  146.     String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);  
  147.     if (contextClassName != null) {  
  148.         try {  
  149.             //如果初始化参数中定义了Web应用程序环境的类名,加载定义的类名  
  150.             return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());  
  151.         }  
  152.         catch (ClassNotFoundException ex) {  
  153.             throw new ApplicationContextException(  
  154.                     "Failed to load custom context class [" + contextClassName + "]", ex);  
  155.         }  
  156.     }  
  157.     else {  
  158.         //如果初始化参数中定义了Web应用程序环境的类名,加载缺省策略中定义的类名,缺省策略保存在ContextLoader.properties文件里  
  159.         contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());  
  160.         try {  
  161.             //加载缺省策略中定义的类名  
  162.             return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());  
  163.         }  
  164.         catch (ClassNotFoundException ex) {  
  165.             throw new ApplicationContextException(  
  166.                     "Failed to load default context class [" + contextClassName + "]", ex);  
  167.         }  
  168.     }  
  169. }  
  170.   
  171. public void closeWebApplicationContext(ServletContext servletContext) {  
  172.     servletContext.log("Closing Spring root WebApplicationContext");  
  173.     try {  
  174.         //如果是可配置的Web应用程序环境  
  175.         if (this.context instanceof ConfigurableWebApplicationContext) {  
  176.             //关闭可配置的Web应用程序环境  
  177.             ((ConfigurableWebApplicationContext) this.context).close();  
  178.         }  
  179.     }  
  180.     finally {  
  181.         //取得当前线程的类加载器  
  182.         ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
  183.         if (ccl == ContextLoader.class.getClassLoader()) {  
  184.             //如果当前线程和本类的公用一个类加载器,则清空静态变量引用  
  185.             currentContext = null;  
  186.         }  
  187.         else if (ccl != null) {  
  188.             //否则根据线程的类加载器移除保存的Web应用程序环境  
  189.             currentContextPerThread.remove(ccl);  
  190.         }  
  191.           
  192.         //移除Servlet环境中的Web应用程序环境的引用  
  193.         servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  
  194.           
  195.         //如果父环境存在则释放父环境  
  196.         if (this.parentContextRef != null) {  
  197.             this.parentContextRef.release();  
  198.         }  
  199.     }  
  200. }  

  

 

 

 

 

 

事实上,根共享环境的加载时同样可以加载一个父环境。尽管这种情况是不常见的,但是Spring Web MVC提供了这样的扩展性。在Servlet初始化参数中可以配置一个Bean工厂路径(locatorFactorySelector),这个Bean工厂路径会被Bean工厂定位器所加载,Bean工厂定位器会在这个Bean工厂中查找以另外一个Servlet参数(parentContextKey)为名字的Bean工厂对象,最后得到的Bean工厂对象则是根共享环境的父环境。如果在初始化参数中没有配置Bean工厂路径,则用缺省的Bean工厂路径classpath*:beanRefFactory.xml。

 

 

[java]  view plain copy
  1. protected ApplicationContext loadParentContext(ServletContext servletContext) {  
  2.     ApplicationContext parentContext = null;  
  3.       
  4.     //取得Web.xml初始化参数配置中对LOCATOR_FACTORY_SELECTOR_PARAM的配置串,这是Bean工厂定位器使用的Bean工厂的路径,如果这个值没有配置,则使用缺省的classpath*:beanRefFactory.xml  
  5.     String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);  
  6.       
  7.     //取得Web.xml初始化参数配置中对LOCATOR_FACTORY_KEY_PARAM的配置串,这是用来取得Bean工厂的关键字  
  8.     String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);  
  9.   
  10.     if (parentContextKey != null) {  
  11.         //locatorFactorySelector如果为空,则使用缺省值classpath*:beanRefFactory.xml初始化Bean工厂定位器  
  12.         BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  
  13.         Log logger = LogFactory.getLog(ContextLoader.class);  
  14.         if (logger.isDebugEnabled()) {  
  15.             logger.debug("Getting parent context definition: using parent context key of '" +  
  16.                     parentContextKey + "' with BeanFactoryLocator");  
  17.         }  
  18.           
  19.   
  20.         //Bean工厂定位器从配置的Bean工厂中找到制定关键字(参数LOCATOR_FACTORY_KEY_PARAM的值) 的工厂  
  21.         this.parentContextRef = locator.useBeanFactory(parentContextKey);  
  22.           
  23.         //进而取得一个应用程序环境,这个应用程序环境作为根共享应用程序环境的父环境  
  24.         parentContext = (ApplicationContext) this.parentContextRef.getFactory();  
  25.     }  
  26.   
  27.     return parentContext;  
  28. }  

 

 

Bean工厂定位器的实现中,加载了一个指定的Bean引用工厂,然后在加载的Bean引用工厂中查找指定名字的Bean工厂对象,这个Bean工厂对象会被返回,作为根共享环境的父环境。这些实现属于Spring 环境项目范围,以下给出序列图,将不在这里做代码注释。

 

 

 

图表 4‑10

 

根据上面的分析,我们发现Spring Web MVC是依赖于Spring环境的定义的,而每一个Spring环境可以最多有一个父环境的引用。这些特点同样应用到了Spring Web MVC的体系结构里。下面我们总结以下Spring Web MVC里面环境的三个层次。其中Servlet专用跟环境和根共享主环境在同一个层次。

 

Servlet专用子环境

 

加载组件:派遣器Servlet(框架Servlet)

配置路径:Servlet初始化参数contextConfigLocation指定的路径

缺省路径:  WEB-INF/[servlet_name] -servlet.xml

保存位置:在框架Servlet对象内部,也以关键字FrameworkServlet全类名.CONTEXT.Servlet保存在Servlet环境里

 

Servlet专用根环境

 

这是一个需要定制实现的组件,组件实现需要把加载的环境以某个关键字保存在Servlet环境里。

 

这样,如果在某个派遣器Servlet初始化参数contextAttribute指定这个关键字, Servlet专用子环境会引用这个加载的专用根环境作为父环境。

 

根共享主环境

 

加载组件:环境加载监听器

配置路径:Servlet环境初始化参数contextConfigLocation指定的路径

缺省路径:  没有缺省路径

保存位置:WebApplicationContext全类名.ROOT

 

根共享环境主环境的父环境

 

加载组件:环境加载监听器和Bean工厂定位器

配置路径:Servlet环境初始化参数locatorFactorySelector指定Bean工厂定位器使用的给BeanFactory,Servlet环境初始化参数parentContextKey指定Bean工厂定位器用于查找BeanFactory的关键字

缺省路径:  parentContextKey的缺省路径是classpath*:beanRefFactory.xml,如果parentContextKey没有制定,则超找所有ApplicationContext的子类实现

保存位置:WebApplicationContext全类名.ROOT

 

除了Servlet专用子环境,其他的父环境都是可选的。根据上面层次的组合,一共有4种环境配置,如下,

 

1.         单个Servlet专用子环境

 

 

图表 4‑11

2.         Servlet专用子环境引用到Servlet专用根环境

 

 

 

图表 4‑12

3.         Servlet专用子环境引用到根共享主环境

 

 

图表 4‑13

 

4.         Servlet专用子环境引用到根共享主环境及其父环境

 

 

 

图表 4‑14

其中,配置1和配置3是我们在开发中经常使用到的。但是在业务逻辑更复杂的情况下,我们可以选择配置2和配置4。配置2能够使多个Servlet共享一个根环境。配置4能使共享的跟环境通过一个Serlvet配置参数转换它的父环境。

 

1.1.4 其他Servlet

 

ResourceServlet是用于存取Web应用程序的内部资源的。它也继承自HttpServletBean,所以能够自动的将Servlet的初始化参数作为属性值来初始化Servlet对象。它改写了HTTP Servlet的Get方法来处理HTTP对资源的请求。

 

HttpRequestHandlerServlet是用来直接将一个HTTP请求转发给HttpRequestHandler。

 

ViewRenderSevlet是用来与Portlet进行集成的Sevlet。

 

http://blog.csdn.net/robertleepeak/article/details/5891750

精品视频课程推荐

透彻理解JavaBean视频教程
深入浅出的讲解JavaBen的写法、JavaBean的用法、JavaBean的实现机制、JavaBean对应翻译的代码理解。

Java数据结构和算法精讲版
本课程专注于数据结构和算法的内容,使用Java来进行代码示例,不空洞的讲解概念和理论,重点放在代码的实现和示例上。 从零开始、全面系统、成体系的讲解数据结构和基本算法,循序渐进的讲述构建软件系统所常见的数据结构和算法。

研磨设计模式——跟着cc学设计系列视频教程
本视频课程是北京Java私塾原创精品书籍《研磨设计模式》一书的配套学习视频,由《研磨设计模式》的第一作者CC录制 课程目标:全面、系统的掌握GoF设计模式的知识,达到可以在实际项目开发中运用的能力 技术要点:如何实现可配置、如何实现缓存以及缓存的管理、如何实现用缓存来控制多实例的创建、如何实现参数化工厂、 如何实现可扩展工厂、如何实现原型管理器、如何实现Java的静态代理和动态代理、如何实现多线程处理队列请求、 如何实现命令的参数化配置、可撤销的操作、宏命令、队列请求和日志请求、如何实现翻页迭代、如何检测环状结构、 如何实现通用的增删改查、如何模拟工作流来处理流程、如何实现简单又通用的XML读取、如何实现模拟AOP的功能......

深入浅出学Shrio视频教程
内容概述:Shiro是目前最热门、最易用、功能超强大的Java权限管理框架,强烈推荐,每个项目都必备的权限管理技术!通过本课程,你将从零开始直到彻底掌握Shiro的相关开发知识,达到可以进行实际项目开发的能力。包括:权限管理基础、Shiro入门、配置、身份认证、授权、Realms、Session管理、和Spring的集成、Web、Cache等众多开发细节技术 技术要点:源码级分析Shiro的授权过程、自定义开发Realm、多个Realms的开发配置、自定义开发AuthenticationStrategy、自定义开发自定义SessionDAO、和Struts2+Spring3的集成(包括修正struts2的bug)、Shiro和SpringMVC+Spring3的集成、包装使用其他的Cache框架、缓存数据同步更新的解决方案等等实际开发中常用的内容

高级软件架构师实战培训阶段一
内容概述:本课程专注于构建:高可扩展性、高性能、大数据量、高并发、分布式的系统架构。 从零开始、全面系统、成体系的软件架构课程,循序渐进的讲述构建上述系统架构所需要的各种技术知识和技能。
技术要点: 1:构建基本的业务功能块,基于Maven+Git+Spring mvc+spring+mybatis+ehcache+mysql+X-gen代码生成
 2:高扩展性的分布式体系架构(基于Nginx+Varnish+Memcache+ActiveMQ)
 3:NoSQL的合理使用和架构优化(基于MongoDB)
 4:分布式文件存储和架构优化(基于MogileFS)

浏览(4826)|评论(0)   交流分类:Java|笔记分类: 深入剖析Spring……

评论(0)
请登录后评论 登录

关于我们 | 联系我们 | 用户协议 | 私塾在线服务协议 | 版权声明 | 隐私保护

版权所有 Copyright(C)2009-2012 私塾在线学习网