Servlet 3.0之Servlet Context

1. ServletContext接口介绍

ServletContext接口定义了servlet运行时Web应用的servlet视图。容器提供者负责提供容器中ServletContext接口的实现。使用ServletContext对象,servlet能够为事件记录日志,获取定位资源的URL引用,以及设置并存储其它servlet能够访问的属性。

ServletContext位于Web服务器大家都熟悉的路径下。例如,一个servlet上下文可以位于http://www.mycorp.com/catalog。所有以/catalog开头的路径,即所谓的上下文路径,会映射到ServletContext相关的Web应用中。

2. 一个ServletContext 接口的范围

部署在容器中的每一个Web应用都关联着一个ServletContext接口的实例对象。对于分布在很多虚拟机器上的容器,一个Web应用中每一个JVM对应一个ServletContext实例。

容器中不作为Web应用一部分部署的servlets,它们隐式地作为一个默认Web应用的一部分,并且有一个默认的ServletContext。在分布式容器中,默认的ServletContext并不是分布式的,且必须仅存在于一个JVM中。

3. 初始化参数

下列ServletContext接口的方法允许servlet访问应用开发者在部署描述符中设置的相关初始化参数。

  • getInitParameter
  • getInitParameterNames

应用开发者用初始化参数来传达配置信息。典型的例子是一个网站站长的电子邮箱或者保存关键数据的系统名称。

4. 配置方法

从servlet3.0开始,下列方法被添加到ServletContext中,用户可以编程方式定义servlet、filter以及它们映射的url模式。这些方法仅能在应用初始化阶段ServletContextListener实现的contextInitialized方法或者ServletContainerInitializer实现的onStartup方法中被调用。

除了添加Servlets或者Filters,也能查找与Servlet、Filter或者Servlet、Filters上的所有Registration对象的map对应的Registration对象的一个实例。

如果传递给ServletContextListener的contextInitialized方法的ServletContext既不是web.xml或web-fragment.xml里面申明的,也不是通过注解@WebListener申明的,那么为servlets,filters和listeners编程配置定义的所有方法必须抛出一个UnsupportedOperationException异常。

  1. 以编程方法添加和配置Servlets
    编程式地为一个上下文添加一个Servlet的能力对于框架开发者来说十分有用。比如,一个框架可以用这个方法申明一个controller servlet。这个方法的返回值是一个ServletRegistration或者一个允许你进一步设置servlet的其它参数,如init-params,url-mapping等等的ServletRegistration.Dynamic对象。这个方法有三个重写版本。

  2. addServlet(String servletName, String className)
    这个方法允许应用编程式地申明一个servlet。它通过指定的名字和类名把servlet添加到servlet上下文中。

  3. addServlet(String servletName, Servlet servlet)
    这个方法允许应用编程式地申明一个servlet。它通过指定的名字和servlet实例添加一个servlet到servlet上下文中。

  4. addServlet(String servletName, Class <? extends Servlet> servletClass)
    这个方法允许应用编程式地申明一个servlet。它通过指定的名字和servlet类的实例添加一个servlet到servlet上下文中。

  5. <T extends Servlet> T createServlet(Class<T> clazz)
    这个方法实例化一个指定的Servlet类。这个方法必须支持除了@WebServlet之外,可应用于Servlets上的所有注解。在通过调用上述的addServlet(String, Servlet)注册到ServletContext之前,方法返回的Servlet实例可以进一步设置。

  6. ServletRegistration getServletRegistration(String, servletName)
    这个方法返回与指定servlet对应的ServletRegistration,或者如果不存在此名字对应的ServletRegistration就返回null。

    如果没有servlets注册到ServletContext,将返回一个空的Map。返回的Map包括与所有申明的和注解的servlet对应的ServletRegistration对象,以及与所有通过addServlet方法添加到ServletContext的servlet对应的ServletRegistration对象。返回的Map的任何变化不得影响ServletContext。

    如果ServletContext 被传递到既不在web.xml或者web-fragment.xml中声明,也没有通过javax.servlet.annotation.WebListener注解的ServletContextListenercontextInitialized方法,那么一个UNsupportedOperationException异常将会抛出。

  7. 以编程方法添加和配置Filters

  8. addFilter(String filterName, String className)
    这个方法允许应用编程式地申明一个filter。它把指定名字和类名的filter添加到web应用中。

  9. addFilter(String filterName, Filter filter)
    这个方法允许通过编程式地申明一个filter。他把指定名字和类名的filter添加到web应用中。

  10. addFilter(String filterName, Class<? extends Filter> filterClass)
    这个方法允许应用通过编程方式申明一个filter。它将会把指定名字和servlet类的实例的filter添加到web应用中。

  11. <T extends Filter> T createFilter(Class<T> clazz)
    这个方法实例化指定的Filter类。这个方法必须支持可应用于Filter上的所有注解。通过调用addServlet(String, Filter)方法注册到ServletContext之前,返回的Filter实例可以进一步配置。指定的Filter类必须定义一个无参构造函数来实例化自己。

  12. FilterRegistration getFilterRegistration(String filterName)
    这个方法返回与之定义filter名字对应的FilterRegistration,或者不存在指定名字的FilterRegistration将返回null。如果ServletContext传递给一个ServletContextListener的contextInitialized方法,但是ServletContextListener既没有在web.xml或者web-fragment.xml中申明,或者没有使用javax.servlet.annotation.WebListener中的注解,那么将会抛出UnsupportedOperationException异常。

  13. Map<String, <? extends FilterRegistration>> getServletRegistrations()
    这个方法返回一个ServletRegistration对象的map,它的key对应所有ServletContext中注册的servlet的名字。如果没有通过ServletContext注册的servlet,那么将返回一个空的map。返回的Map包含了与所有申明的和注解的servlet对应的ServletRegistration对象,同时也包括通过任意addServlet方法添加的所有servlets对应的ServletRegistration对象。返回的Map的任何变化不影响ServletContext。如果ServletContext传递给一个ServletContextListener的contextInitialized方法,但是ServletContextListener既没有在web.xml或者web-fragment.xml中申明,也没有通过javax.servlet.annotation.WebListener注解,那么将会抛出UnsupportedOperationException异常。

  14. 编程式地添加和配置listeners

  15. void addListener(String className)
    添加指定类名的listener到ServletContext。指定类名的类将通过ServletContext中与应用相关的类加载器加载,而且必须实现下列一个或多个的接口:

 * javax.servlet.ServletContextAttributeListener
 * javax.servlet.ServletRequestListener
 * javax.servlet.ServletRequestAttributeListener
 * javax.servlet.http.HttpSessionLister
 * javax.servlet.http.HttpSessionAttributeListener

 如果ServletContext被传递给ServletContainerInitializer的onStartup方法,除了上述接口,指定名字的类也可以实现*javax.servlet.ServletContextListener*。作为这个方法调用的一部分,容器必须加载指定类名的类来确保它实现了必要接口中的一个。如果指定listener是一个调用顺序与声明顺序一致的listener接口的实例,也就是说,如果它实现了*javax.servlet.ServletRequestListener* 或者*javax.servlet.http.HttpSessionListener*,那么新的listener将会被添加到那个接口的listeners的有序列表的末尾。

1. **<T extends EventListener> void addListener(T t)**

添加指定listener到ServletContext。指定的listener必须是下列一个或者多个接口的实例:
* javax.servlet.ServletContextAttributeListener
* javax.servlet.ServletRequestListener
* javax.servlet.ServletRequestAttributeListener
* javax.servlet.http.HttpSessionListener
* javax.servlet.http.HttpSessionAttributeListener

  如果ServletContext被传递给ServletContainerInitializer的onStartup方法,除了上述接口,指定listener也可以是*javax.servlet.ServletContextListener*的实例。
 如果指定listener是一个调用顺序与声明顺序一致的listener接口的实例,也就是,如果它实现了*javax.servlet.ServletRequestListener*或者*javax.servlet.http.HttpSessionListener*,那么新的listener将会添加到那个接口的listener的有序列表的末尾。
1. **void addListener(Class<? extends EventListener> listenerClass)**

添加指定类类型的listener到ServletContext。指定listener类必须实现一个或者多个下列接口:
* javax.servlet.ServletContextAttributeListener
* javax.servlet.ServletRequestListener
* javax.servlet.ServletRequestAttributeListener
* javax.servlet.http.HttpSessionListener
* javax.servlet.http.HttpSessionAttributeListener

 如果ServletContext被传递给ServletContainerInitializer的onStartup方法,除了上述接口,指定listener类也可以实现*javax.servlet.ServletContextListener*。

如果指定listener类实现了一个调用顺序与声明顺序一致的listener,也就说,如果它实现了javax.servlet.ServletRequestListener或者javax.servlet.http.HttpSessionListener,那么新的listener将会被添加到那个接口的listener的有序列表的末尾。

  1. <T extends EventListener> void createListener(Class<T> clazz)
    这个方法实例化指定的EventListener类。具体的EventListener类必须实现下列至少一个接口:

    • javax.servlet.ServletContextAttributeListener
    • javax.servlet.ServletRequestListener
    • javax.servlet.ServletRequestAttributeListener
    • javax.servlet.http.HttpSessionListener
    • javax.servlet.http.HttpSessionAttributeListener

    这个方法必须支持适用于本规范中定义的上述listener接口的所有注解。在它调用addListener(T t)注册到ServletContext之前,返回的EventListener实例可以进一步被设置。指定的EventListener类必须定义一个用来实例化自己的无参构造函数。

  2. 对于以编程方式添加的Servlets、Filters和Listeners的注解处理要求
    当使用编程的API添加一个servlet或者创建一个servlet,除去需要一个实例参数的addServlet,下列注解必须在讨论的和定义元数据的类中被检查,除非它们通过调用ServletRegistration.Dynamic/ServletRegistration的API被重写:

 * @ServletSecurity
 * @RunAs
 * @DeclareRoles
 * @MultipartConfig

 对于Filters和Listener,没有注解需要被检查。

所有编程式添加或者编程式创建,而并不是通过接受一个实例参数的方法来添加的组件(Servlets, Filters, Listeners)上的资源注入,仅当组件是一个Managed Bean时才会被支持。
想知道更多关于什么是Managed Bean的详细信息请参考Java EE 6部分和JSR 299中定义Managed Bean规范。

5. Context属性

一个servlet能把一个对象属性通过名字绑定到上下文中。任何绑定到上下文的属性,相同Web应用中的其它任何servlet都可访问。下列ServletContext接口的方法允许访问该功能:

  • setAttribute
  • getAttribute
  • getAttributeNames
  • removeAttribute
  1. 分布式容器中的上下文属性
    上下文属性位于它们被创建的JVM。这防止在分布式环境中ServletContext属性被共享。分布式环境中,当信息需要在servlet中共享,这些信息应该放在session、存放于数据库或者在企业JavaBean组件中设置。

6. 资源

ServletContext接口通过它下列方法,仅对是Web应用一部分的静态文件提供直接访问,包括HTML、GIF以及JPEG文件。

  • getResource
  • getResourceAsStream

getResource和getResourceAsStream方法接收一个'/'开头的字符串参数,这个参数指定了资源相对于上下文根路径,或者相对于web应用WEB-INF/lib目录里JAR文件的META-INF/resource目录的资源路径。在查找WEB-INF/lib目录里任何JAR文件之前,这些方法将首先为请求的资源搜索web应用的根路径。WEB-INF/lib目录里JAR文件被扫描的顺序没有定义。文档的层次结构可能存在服务器的文件系统,Web应用的归档文件,远端服务器或者其它地方。

这些方法不用来获取动态内容。
比如,在支持JSP规范的容器中,表单getResource("index.jsp")的方法调用将返回JSP源码,而不是处理过的输出。
第9章中,Dispatching Requests将会讨论更多访问动态内容。

Web应用中完整的资源列表能通过调用getResourcePaths(String path) 方法被访问到。关于这个方法的语义详情可以在这个规范的API文档中找到。

7. 多个主机和Servlet上下文

Web服务器可以在一个服务器上支持多个共享一个IP地址的逻辑主机。这种能力有时被称之为虚拟主机。在这种场景下,每个逻辑主机一定有它自己的servlet上下文或者servlet上下文的集合。Servlets上下文不能在虚拟主机之间被共享。

8. 重新加载的注意事项

对于便捷开发,虽然一个容器提供者实现一个类重新加载方案并不是必须的,但是任何实现必须确保所有的servlets以及它们使用的类在单个类加载器范围内被加载。这个需求需要用来确保应用按照开发者期望的方式运行。
作为开发辅助,通知绑定了监听者的session的完整语义应该被容器支持,以便类重新加载时监控会话终止。

以前的一些容器会创建新的类加载器来加载一个servlet,以便区分于用来加载其它servlet的类加载器或者servlet上下文中使用的类。这会引起servlet上下文中的对象引用指向不是预期类或者对象,并且会引起不确定的行为。这个要求用来防止因需求产生的新的类加载器的问题。

  1. 临时工作目录
    临时存储目录对于每一个servlet上下文都是必要的。
    Servlet容器必须为每一个servlet上下文提供一个临时目录,并且让它可以通过javax.servlet.context.tempdir上下文属性被访问到。此属性相关的对象必须是java.io.File 类型。

这个要求在许多servlet引擎实现中提供了共同的便利。当servlet容器启动时,容器并不要求维护临时目录的内容,但是被要求来确保在servlet容器中,一个servlet容器的的临时目录内容对其它Web应用的servlet上下文不可见。

翻译自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容