Servlet规范之Servlet Context

Posted 顧棟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet规范之Servlet Context相关的知识,希望对你有一定的参考价值。

Servlet Context

文章目录

ServletContext接口介绍

ServletContext接口定义了Servlet对其运行的Web应用程序的视图。容器提供者负责在Servlet容器中提供ServletContext接口的实现。使用 ServletContext对象,servlet可以记录事件,获得资源的URL引用,并设置和存储上下文中其他servlet可以访问的属性。

一个ServletContext根植于Web服务器中的一个已知路径。例如,一个Servlet上下文可以位于http://www.mycorp.com/catalog。所有以/catalog请求路径开始的请求,被称为上下文路径,被路由到与ServletContext相关的Web应用程序。

ServletContext接口的范围

每个部署在容器中的Web应用都有一个ServletContext接口的实例对象。在容器分布在许多虚拟机上的情况下,Web应用程序将为每个JVM拥有一个 ServletContext的实例。

容器中的Servlet如果没有作为Web应用程序的一部分被部署,就隐含地成为 "默认 "Web应用程序的一部分,并有一个默认的ServletContext。在分布式容器中,默认的ServletContext是不可分配的,必须只存在于一个JVM中。

初始化参数

ServletContext接口的下列方法允许servlet访问与Web应用程序相关的上下文初始化参数,这些参数由应用程序开发人员在部署描述符中指定。

  • getInitParameter
  • getInitParameterNames

初始化参数被应用开发者用来传达设置信息。典型的例子是一个网站管理员的电子邮件地址,或一个保存关键数据的系统的名称。

配置方法

自Servlet 3.0以来,以下方法被添加到ServletContext中,以便能够以编程方式定义Servlet、filters和它们所映射的url模式。这些方法只能在应用程序的初始化过程中,从ServletContextListener实现的contexInitialized方法或ServletContainerInitializer实现的onStartup方法中调用。

除了添加Servlet和Filter,人们还可以查找对应于Servlet 或Filter的Registration对象的实例 或Servlet或Filter的所有Registration对象的map。

如果ServletContext传递给ServletContextListener的contextInitialized方法的ServletContextListener既没有在web.xmlweb-fragment.xml中声明,也没有用@WebListener注释,那么对于所有在ServletContext中定义的用于编程配置Servlet、filters和Listeners的方法必须抛出不支持的操作异常

以编程方式添加和配置Servlets

以编程方式将servlet添加到上下文中的能力对于框架开发者来说是非常有用的。例如,一个框架可以使用这个方法声明一个控制器servlet。该方法的返回值是一个 ServletRegistrationServletRegistration.Dynamic对象,它允许你进一步设置Servlet的其他参数,如init-paramsurl-mappings等。该方法有三个重载版本,如下所述。

  1. addServlet(String servletName, String className)这个方法允许应用程序以编程方式声明一个servlet。它将具有给定名称和类名称的Servlet添加到Servlet上下文中。
  2. addServlet(String servletName, Servlet servlet)这个方法允许应用程序以编程方式声明一个servlet。它将具有给定名称的Servlet和Servlet实例添加到Servlet上下文中。
  3. addServlet(String servletName, Class<? extends Servlet> servletClass) 这个方法允许应用程序以编程方式声明一个servlet。它将具有给定名称的servlet和servlet类的一个实例添加到servlet上下文中。
  4. <T extends Servlet> T createServlet(Class<T> c)这个方法实例化给定的Servlet类。该方法必须支持所有适用于Servlet的注解,除了@WebServlet。在通过调用上面定义的addServlet(String, Servlet)向ServletContext注册之前,返回的Servlet实例可以被进一步定制。
  5. ServletRegistration getServletRegistration(String servletName)该方法返回与给定名称的servlet对应的servletregiregistration,如果该名称下不存在ServletRegiregistration,则返回null。如果ServletContext被传递到既没有在web.xmlweb-fragment.xml中声明也没有用javax.servlet.annotation.WebListener注释的ServletContextListenercontextInitialized方法,则抛出UnsupportedOperationException
  6. Map<String, ? extends ServletRegistration> getServletRegistrations()该方法返回一个ServletRegistration对象的映射,该映射以名称为键,对应于在ServletContext注册的所有Servlet。如果没有ServletContext注册的Servlet,则返回一个空Map。返回的Map包括对应于所有声明和注释的ServletRegistration对象,以及对应于所有通过addServlet方法添加的Servlet的ServletRegistration对象。对返回的Map的任何改变必须不影响ServletContext。如果ServletContext被传递给ServletContextListenercontextInitialized方法,而该方法既没有在web.xmlweb-fragment.xml中声明,也没有用javax.servlet.annotation.WebListener注释,则会抛出UnsupportedOperationException

以编程方式添加和配置Filters

  1. addFilter(String filterName, String className)
  2. addFilter(String filterName, Filter filter)
  3. addFilter(String filterName, Class<? extends Filter> filterClass)
  4. <T extends Filter> T createFilter(Class<T> c)
  5. FilterRegistration getFilterRegistration(String filterName)
  6. Map<String, ? extends FilterRegistration> getFilterRegistrations()

以编程方式添加和配置Listeners

  1. addListener(String className)ServletContext添加具有给定类名称的监听器。具有给定名称的类将使用与ServletContext所代表的应用程序相关的类加载器来加载,并且必须实现以下一个或多个接口:

    1. javax.servlet.ServletContextAttributeListener
    2. javax.servlet.ServletRequestListener
    3. javax.servlet.ServletRequestAttributeListener
    4. javax.servlet.http.HttpSessionListener
    5. javax.servlet.http.HttpSessionAttributeListener
    6. javax.servlet.http.HttpSessionIdListener

    如果 ServletContext 被传递给 ServletContainerInitializer 的 onStartup 方法,那么除了上面列出的接口外,具有指定名称的类还可能实现 javax.servlet.ServletContextListener。作为这个方法调用的一部分,容器必须加载具有指定类名的类,以确保它实现了所需的接口之一。如果具有指定名称的类实现了一个监听器接口,其调用顺序与声明顺序一致
    顺序对应于声明顺序,也就是说,如果它实现了javax.servlet.ServletRequestListenerjavax.servletContextListenerjavax.servlet.http.HttpSessionListener,那么新的监听器将被添加到该接口的监听器的顺序列表的最后。

  2. <T extends EventListener> void addListener(T t)将给定的监听器添加到ServletContext中。给定的监听器必须是以下一个或多个接口的实例。

    1. javax.servlet.ServletContextAttributeListener
    2. javax.servlet.ServletRequestListener
    3. javax.servlet.ServletRequestAttributeListener
    4. javax.servlet.http.HttpSessionListener
    5. javax.servlet.http.HttpSessionAttributeListener
    6. javax.servlet.http.HttpSessionIdListener

    如果ServletContext被传递给ServletContainerInitializer的onStartup方法,那么除了上面列出的接口外,给定的监听器也可能是javax.servlet.ServletContextListener的实例。如果给定的监听器是一个监听器接口的实例,其调用顺序与声明顺序一致,也就是说,如果它实现了javax.servlet.ServletRequestListener、javax.servletContextListener或javax.servlet.http.HttpSessionListener,那么新的监听器将被添加到该接口的监听器的有序列表的最后。

  3. addListener(Class<? extends EventListener> listenerClass)在ServletContext中添加给定类别类型的监听器。给定的监听器必须是以下一个或多个接口的实例。

    1. javax.servlet.ServletContextAttributeListener
    2. javax.servlet.ServletRequestListener
    3. javax.servlet.ServletRequestAttributeListener
    4. javax.servlet.http.HttpSessionListener
    5. javax.servlet.http.HttpSessionAttributeListener
    6. javax.servlet.http.HttpSessionIdListener

    如果ServletContext被传递给ServletContainerInitializer的onStartup方法,那么除了上面列出的接口外,给定的监听器也可能是javax.servlet.ServletContextListener的实例。如果给定的监听器是一个监听器接口的实例,其调用顺序与声明顺序一致,也就是说,如果它实现了javax.servlet.ServletRequestListener、javax.servletContextListener或javax.servlet.http.HttpSessionListener,那么新的监听器将被添加到该接口的监听器的有序列表的最后。

  4. <T extends EventListener> T createListener(Class<T> c)该方法实例化给定的EventListener类。指定的EventListener类必须至少实现以下接口之一:

    1. javax.servlet.ServletContextAttributeListener
    2. javax.servlet.ServletRequestListener
    3. javax.servlet.ServletRequestAttributeListener
    4. javax.servlet.http.HttpSessionListener
    5. javax.servlet.http.HttpSessionAttributeListener
    6. javax.servlet.http.HttpSessionIdListener

    该方法必须支持本规范所定义的适用于上述监听器接口的所有注释。在通过调用addListener(T t)向ServletContext注册之前,返回的EventListener实例可以被进一步定制。给定的EventListener类必须定义一个零参数构造函数,用于实例化它。

以编程方式添加的servlet、Filters和Listeners的注释处理需求

当使用编程式API添加一个servlet或创建一个servlet时,除了addServlet需要一个实例外,必须在相关的类中自检以下注释,并且必须使用其中定义的元数据,除非它被ServletRegistration.Dynamic/ServletRegistration中的API调用重写。

@ServletSecurity, @RunAs, @DeclareRoles, @MultipartConfig.

For Filters and Listeners no annotations need to be introspected.

对于过滤器和监听器来说,不需要对注解进行自检。

对所有以编程方式添加或以编程方式创建的组件(Servlet、过滤器和监听器)的资源注入,除了通过获取实例的方法添加的组件外,只有当该组件是 CDI 托管 Bean 时才会被支持。详情请参考15.5.15节 “Contexts and Dependency Injection for Java EE requirements”(15-197页)。

Context Attributes

一个servlet可以通过名称将一个对象属性绑定到上下文中。绑定到上下文中的任何属性对属于同一Web应用程序的任何其他Servlet都是可用的。ServletContext接口的下列方法允许访问这一功能:

  • setAttribute
  • getAttribute
  • getAttributeNames
  • removeAttribute

###分布式容器中的上下文属性

上下文属性是创建它们的JVM的本地属性。这使得ServletContext属性无法成为分布式容器中的共享内存存储。当信息需要在分布式环境中运行的Servlet之间共享时,信息应该被放置在一个会话中(见第7章,“会话”),存储在一个数据库中,或者设置在一个Enterprise JavaBeans™组件中。

Resources

ServletContext接口只提供对静态内容文件层次的直接访问,这些文件是Web应用程序的一部分,包括html、GIF和JPEG文件,通过ServletContext接口的以下方法:

  • getResource
  • getResourceAsStream

getResourcegetResourceAsStream方法接受一个带"/"的String作为参数,给出相对于上下文根的资源路径或相对于Web应用WEB-INF/lib目录下的JAR文件的META-INF/resources目录。这些方法将首先在Web应用程序上下文的根部搜索所请求的资源,然后再查看WEB-INF/lib目录下的任何JAR文件。WEB-INF/lib目录下的JAR文件的扫描顺序是未定义的。这个层次的文件可能存在于服务器的文件系统中、Web应用程序的归档文件中、远程服务器上或其他位置。

这些方法不是用来获取动态内容的。例如,在一个支持JavaServer Pages™规范1的容器中,形式为getResource("/index.jsp")的方法调用将返回JSP源代码而不是处理后的输出。关于访问动态内容的更多信息,请参阅第9章 “Dispatching Requests”。可以使用getResourcePaths(String path)方法访问Web应用程序中资源的完整列表。关于这个方法的语义的全部细节可以在本规范的API文档中找到。

多个主机和Servlet上下文

网络服务器可以支持多个逻辑主机在一台服务器上共享一个IP地址。这种能力有时被称为 “虚拟主机”。在这种情况下,每个逻辑主机必须有自己的Servlet上下文或一组Servlet上下文。Servlet上下文不能跨虚拟主机共享。

ServletContext接口的getVirtualServerName方法允许访问ServletContext所部署的逻辑主机的配置名称。Servlet容器可以支持多个逻辑主机。该方法必须为部署在逻辑主机上的所有Servlet上下文返回相同的名称,该方法返回的名称必须是独特的,每个逻辑主机都是稳定的,并且适合用于将服务器配置信息与逻辑主机相关联。

重新载入的考虑因素

尽管不要求容器提供者为方便开发而实现类重载方案,但任何这样的实现都必须确保所有的servlet和它们可能使用的类都在一个单一的类加载器的范围内加载。这一要求是必要的,以保证应用程序的行为符合开发者的预期。作为开发的辅助手段,容器应该支持向会话绑定监听器发出通知的全部语义,以便在类重新加载时监控会话终止。

前几代的容器创建了新的类加载器来加载servlet,与用于加载其他servlet或servlet上下文中的类的加载器不同。这可能会导致servlet上下文中的对象引用指向意外的类或对象,并导致意外行为。这个要求是必要的,以防止因要求生成新的类加载器而引起的问题

临时工作目录

每个servlet上下文都需要一个临时存储目录。Servlet容器必须为每个servlet上下文提供一个私有的临时目录,并通过javax.servlet.context.tempdir上下文属性使其可用。与该属性相关的对象必须是java.io.File类型的。

这一要求承认了许多servlet引擎实现中提供的一种常见的便利。容器不需要在 servlet 容器重新启动时维护临时目录的内容,但需要确保一个 servlet 上下文的临时目录的内容对在 servlet 容器上运行的其他 Web 应用程序的 servlet 上下文不可见。

以上是关于Servlet规范之Servlet Context的主要内容,如果未能解决你的问题,请参考以下文章

Servlet规范之预览

Servlet规范之Servlet顶层接口

Servlet编程专题10之Servlet3.0注解

Servlet编程专题8之Servlet规范中的监听器

Servlet编程专题9之Servlet规范中的过滤器

Servlet规范之安全