servlet3.1规范: 第8章 注解和可插拔性

来源:互联网 发布:宝马编程是什么意思 编辑:程序博客网 时间:2024/06/05 20:47

转载: Servlet规范

注解和可插拔性

本章讨论在web应用中使用的Servlet 3.0规范定义的注解和启用框架和库的可插拔性增强。

8.1 注解和可插拔性

在web应用中,使用注解的类仅当它们位于WEB-INF/classes目录中,或它们被打包到位于应用的WEB-INF/lib中的jar文件中时它们的注解才将被处理。

Web应用部署描述符的web-app元素包含一个新的”metadata-complete”属性。”metadata-complete”属性定义了web描述符是否是完整的,或是否应该在部署时检查jar包中的类文件和web fragments。如果”metadata-complete”设置为”true”,部署工具必须忽略存在于应用的类文件中的所有servlet注解和web fragments。如果metadata-complete属性没有指定或设置为”false”,部署工具必须检查应用的类文件的注解,并扫描web fragments。

兼容Servlet3.0的web容器必须支持下面的注解。

8.1.1 @WebServlet

该注解用于在Web应用中定义Servlet组件。该注解在一个类上指定并包含关于声明的Servlet的元数据。必须指定注解的urlPatterns或value属性。所有其他属性是可选的默认设置(请参考javadoc获取更多细节)。当注解上唯一属性是url模式时推荐使用value,当使用了其他属性时使用urlPatterns属性。在同一注解上同时使用value和urlPatterns属性是非法的。如果没有指定Servlet名字则默认是全限定类名。被注解的servlet必须指定至少一个url模式进行部署。如果同一个Servlet类以不同的名字声明在部署描述符中,必须实例化一个新的Servlet实例。如果同一个Servlet类使用定义在4.4.1节”编程式添加和配置Servlet”的编程式API添加到ServletContext,使用@WebServlet注解声明的值必须被忽略,必须创建一个指定名字的Servlet的新的实例。

@WebServlet注解的类必须继承javax.servlet.http.HttpServlet类。
下面是如何使用该注解的一个示例。
代码示例8-1 @WebServlet 注解示例

@WebServlet(”/foo”)  public class CalculatorServlet extends HttpServlet{     //...  }

下面是如何使用该注解指定更多的属性的一个示例。
代码示例 8-2 @WebServlet 注解示例使用其它指定的注解属性

@WebServlet(name=”MyServlet”, urlPatterns={"/foo", "/bar"})  public class SampleUsingAnnotationAttributes extends HttpServlet{     public void doGet(HttpServletRequest req, HttpServletResponse res) {      ...     }}  

8.1.2 @WebFilter

该注解用于在Web应用中定义Filter。该注解在一个类上指定且包含关于声明的过滤器的元数据。如果没有指定Filter名字则默认是全限定类名。注解的urlPatterns属性, servletNames属性或value属性必须被指定。所有其他属性是可选的默认设置(请参考javadoc获取更多细节)。当注解上唯一属性是url模式时推荐使用value,当有使用其他属性时使用urlPatterns属性。在同一注解上同时使用value和urlPatterns属性是非法的。

@WebFilter注解的类必须实现javax.servlet.Filter。

下面是如何使用该注解的一个示例。
代码示例 8-3 @WebFilter 注解示例

@WebFilter(“/foo”)  public class MyFilter implements Filter {     public void doFilter(HttpServletRequest req, HttpServletResponse) {      ...     }  }

8.1.3 @WebInitParam

该注解用于指定必须传递到Servlet或Filter的任何初始化参数。它是WebServlet和WebFilter注解的一个属性。

8.1.4 @WebListener

WebListener注解用于注解获得特定web应用上下文中的各种操作事件的监听器。@WebListener注解的类必须实现以下接口:

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

示例:

@WebListener  public class MyListener implements ServletContextListener{     public void contextInitialized(ServletContextEvent sce) {        ServletContext sc = sce.getServletContext();        sc.addServlet("myServlet",                    "Sample servlet",                    "foo.bar.MyServlet",                    null,                    -1);      sc.addServletMapping("myServlet", new String[] { "/urlpattern/*" });     }  }  

8.1.5 @MultipartConfig

该注解,当指定在Servlet上时,表示请求期望是mime/multipart类型。相应servlet的HttpServletRequest对象必须使用getParts和getPart方法遍历各个mime附件以获取mime附件。javax.servlet.annotation.MultipartConfig的location属性和<multipart-config><location>元素被解析为一个绝对路径且默认为javax.servlet.context.tempdir。如果指定了相对地址,它将是相对于tempdir位置。绝对路径与相对地址的测试必须使用java.io.File.isAbsolute。

8.1.6 其他注解/惯例

除了这些注解,15.5节的”注解和资源注入”中的所有注释将继续工作在这些新注解上下文中。

默认情况下,所有应用的welcome-file-list列表中都将有index.htm(l)和index.jsp。该描述符可以用来覆盖这些默认设置。

当使用注解时,没有指定从WEB-INF/classes或WEB-INF/lib目录下的各种框架jar包/类中加载监听器、Servlet的顺序。如果顺序是很重要的,那么请看web.xml模块部分和后面的web.xml和web-fragment.xml顺序部分。顺序仅能在部署描述符中指定。

8.2 可插拔性

8.2.1 web.xml模块

使用上述定义的注解,使得使用web.xml可选。然而,对于覆盖默认值或使用注解设置的值,需要使用部署描述符。如前所述,如果web.xml 描述符中的metadata-complete元素设置为true,将不会处理在class文件和绑定在jar包中的web-fragments中的注解。这意味着,应用的所有元数据通过web.xml描述符指定。

为了给开发人员更好的可插拔性和更少的配置,在这个版本(Servlet3.0)的规范中,我们引入了web模块部署描述符片段(web fragment)的概念。web fragment是web.xml的部分或全部,可以在一个类库或框架 jar包的META-INF目录指定和包括。在WEB-INF/lib目录中的普通的老的jar文件即使没有web-fragment.xml也被认为是一个fragment,它们之中指定的所有注解都将按照定义在8.2.3节的规则处理,容器将会取出并按照如下定义的规则进行配置。

web fragment是web应用的一个逻辑分区,以这样一种方式,web应用中使用的框架可以定义所有自定义配置(artifact)而无需要求开发人员在web.xml中编辑或添加信息。它几乎包含web.xml描述符中使用的所有相同的元素。不过描述符的顶级元素必须是web-fragment且对应的描述符文件必须命名为web-fragment.xml,相关元素的顺序在web-fragment.xml 和 web.xml也是不同的,请参考定义在第14章的部署描述符一章中对应的web-fragment模式(schema)。

如果框架打包成jar文件,且有部署描述符形式的元数据信息,那么web-fragment.xml描述符必须在该jar包的META-INF/目录中。

如果框架想使用META-INF/web-fragment.xml,以这样一种方式,它扩充了web应用的web.xml,框架必须被绑定到Web应用的WEB-INF/lib目录中。为了使框架中的任何其他类型的资源(例如,类文件)对web应用可用,把框架放置在web应用的classloader委托链的任意位置即可。换句话说,只有绑定到web应用的WEB-INF/lib目录中的JAR文件,需要扫描其web-fragment.xml,而那些在类装载委托链之上的JAR文件不需要扫描其web-fragment.xml。

在部署期间,容器负责扫描上面指定的位置和发现web-fragment.xml并处理它们。存在于当前单个web.xml的名字唯一性的要求,也同样适用于一组web.xml和所有可应用的web-fragment.xml文件。

如下是库或框架可以包括什么的例子。

<web-fragment>    <servlet>      <servlet-name>welcome</servlet-name>      <servlet-class>        WelcomeServlet      </servlet-class>    </servlet>    <listener>      <listener-class>           RequestListener     </listener-class>    </listener>  </web-fragment>

以上的web-fragment.xml将被包括在框架的jar文件的META-INF/目录中。web-fragment.xml配置和应该应用的注解的顺序是未定义的。如果顺序对于某一应用是很重要的方面,请参考下面如何实现所需的顺序定义的规则。

8.2.2 web.xml和web-fragment.xml顺序

由于规范允许应用配置资源由多个配置文件组成(web.xml 和 web-fragment.xml),必须解决从应用的多个不同位置发现和加载配置文件的顺序问题。本节详述了配置资源的作者如何声明他们自定义配置(artifact)的顺序要求。

web-fragment.xml可以有一个javaee:java-identifierType类型的顶级<name>元素,且在一个web-fragment.xml中仅能有一个<name>元素。如果存在一个<name>元素,它必须考虑artifact的顺序(除非出现重复名异常,如上文所述)。

在下面两种情况下必须考虑允许应用程序配置资源表达它们的优先顺序。
1). 绝对顺序:在web.xml中的<absolute-ordering>元素。在一个web.xml中仅能有一个<absolute-ordering>元素。

a). 在这种情况下,下面第二种情况处理的优先顺序必须被忽略。
b). web.xml和WEB-INF/classes必须在absolute-ordering元素中的所有web-fragment之前处理。
c). <absolute-ordering>的所有直接子元素<name>必须解释为表示那些命名为web-fragment的绝对顺序,它可能存在,也可能不存在,必须被处理。
d). <absolute-ordering>可以包含零个或一个<others/>元素。下面描述此元素必需的功能。如果<absolute-ordering>元素没有包含<others/>元素,没有在<name/>明确提到的web-fragment必须被忽略。不会扫描被排除的jar包中的注解Servlet、Filter或Listener。然而,如果是被排除的jar包中的servlet、filter或listener,其列在web.xml或非排除的web-fragment.xml中,那么将使用它的注解除非另外使用metadata-complete排除。在排除的jar包的TLD 文件中发现的ServletContextListener不能使用编程式API配置Filter和Servlet,任何试图这样做将导致IllegalStateException。如果发现的ServletContainerInitializer是从一个被排除的jar包中装载的,它将被忽略。不扫描被排除的jar包中的任何ServletContainerInitializer处理的类。
e). 重复名字异常:如果,当遍历<absolute-ordering>子元素时,遇到多个子元素具有相同<name>元素,只需考虑首次出现的。

2). 相对顺序:在web-fragment.xml中的<ordering>元素,一个web-fragment.xml只能有一个<ordering>元素。

a). web-fragment.xml可以有一个<ordering>元素。如果是这样,该元素必须包含零个或一个<before>元素和零个或一个<after>元素。这些元素的含义在下面进行说明。
b). web.xml和WEB-INF/classes 必须在ordering元素中的所有web-fragment之前处理。
c). 重复命名异常:如果,当遍历web-fragments时,遇到多个成员具有相同<name>元素,应用必须记录包含有助于解决这个问题的错误提示信息,且部署必须失败。例如,一种解决该问题的办法是用户使用绝对顺序,在这种情况下将忽略相对顺序。
d). 思考这个简短但具说明性的例子。3个作为应用一部分的web-fragment - MyFragment1、 MyFragment2 和 MyFragment3,也包括一个web.xml。

web-fragment.xml  <web-fragment>    <name>MyFragment1</name>    <ordering><after><name>MyFragment2</name></after></ordering>    ...  </web-fragment>  
web-fragment.xml  <web-fragment>    <name>MyFragment2</name>    ..  </web-fragment>  
web-fragment.xml  <web-fragment>    <name>MyFragment3</name>    <ordering><before><others/></before></ordering>    ..  </web-fragment>  
web.xml  <web-app>    ...  </web-app>  

在该示例中,处理顺序将是:

web.xml
MyFragment3
MyFragment2
MyFragment1

前面的示例说明了一些,但不是全部原则,以下是全部原则。

<before>意味着文档必须被安排在指定在嵌套<name>元素的name匹配的文档之前。
<after>意味着文档必须被安排在指定在嵌套<name>元素的name匹配的文档之后。
■ 在<before><after>可以包括特殊的<others/>元素零次或一次,或直接包括在<absolute-ordering>元素中零次或一次。必须按下面的方式处理<others/>元素。
■ 如果<before>元素包含一个嵌套的<others/>,该文档将被移动到有序的文档列表开头。如果有多个文档指定<before><others/>,则它们都将在有序的文档列表开头,但未指定该组文档的顺序。
■ 如果<after>元素包含一个嵌套的<others/>,该文档将被移动有序的文档列表末尾。如果有多个文档指定<after><others/>,则它们都将在有序的文档列表末尾,但未指定该组文档的顺序。
■ 在一个的<before><after>元素内,如果存在一个<others/>元素,但在它的父元素内<name>元素不是唯一的,父元素内的其他元素必须按照顺序处理。
■ 如果<others/>直接出现在<absolute-ordering>内,在运行时阶段必须确保没有明确指定在<absolute-ordering>部分的所有web-fragment包含在处理顺序中。
■ 如果web-fragment.xml 文件没有<ordering>或web.xml 没有<absolute-ordering>元素,则artifact被假定没有任何顺序依赖。
■如果运行时发现循环引用,必须记录有用的提示信息,且应用程序必须部署失败。此外,用户可能采取的一种措施是在web.xml使用绝对路径。
■ 之前的示例可以被扩展以说明当web.xml包含顺序部分的情况。

web.xml

<web-app>  <absolute-ordering>      <name>MyFragment3</name>      <name>MyFragment2</name>  </absolute-ordering>    ...  </web-app> 

在该示例中,各种元素的顺序将是:

web.xml
MyFragment3
MyFragment2

下面包括了一些额外的示例场景。所有这些适用于相对顺序而不是绝对顺序。

Document A:

<after>    <others/>      <name>        C      </name>  </after>  

Document B:

<before>     <others/>  </before>  

Document C:

<after>     <others/>  </after>  

Document D: 没有指定顺序
Document E: 没有指定顺序
Document F:

<before>    <others/>    <name>      B    </name>  </before>  

产生的解析顺序:
web.xml, F, B, D, E, C, A。

Document<no id>

<after>    <others/>  </after>  <before>    <name>    C    </name>  </before>  

Document B:

<before>    <others/>  </before> 

Document C:没有指定顺序
Document D:

<after>    <others/>  </after>  

Document E:

<before>    <others/>  </before>  

Document F:没有指定顺序

产生的解析顺序可能是下列之一:
■ B, E, F, , C, D
■ B, E, F, , D, C
■ E, B, F, , C, D
■ E, B, F, , D, C
■ E, B, F, D, , C
■ B, E, F, D, , C

Document A:

<after>    <name>    B    </name>  </after>  

Document B:没有指定顺序
Document C:

<before>    <others/>  </before> 

Document D: 没有指定顺序

产生的解析顺序: C, B, D, A。解析的顺序也可能是: C, D, B, A 或 C, B, A, D

8.2.3 从web.xml、web-fragment.xml和注解装配描述符

如果对于一个应用来说Listener、Servlet和Filter的调用顺序是很重要的,那么必须使用部署描述符。同样,如果有必要,可以使用上面定义的顺序元素。如上所述,当使用注解定义Listener、Servlet和Filter,它们调用的顺序是未指定的。下面是用于装配应用程序的最终部署描述符的一组规则:

1.如果有关的Listener、Servlet和Filter的顺序必须指定,那么必须指定在web-fragment.xml或web.xml。

2.顺序将依据它们定义在描述符中的顺序,和依赖于web.xml中的absolute-ordering元素或web-fragment.xml中的ordering元素,如果存在。

1). 匹配请求的过滤器链的顺序是它们在web.xml中声明的顺序。
2). Servlet在请求处理时延迟实例化或在部署时立即实例化。在后一种情况,以它们的load-on-startup元素表示的顺序实例化。
3). 在此规范发布这前,上下文Listener以随机顺序调用。在Servlet3.0,Listener以它们在web.xml中声明的顺序调用,如下所示:

a). javax.servlet.ServletContextListener实现的contextInitialized方法以声明时顺序调用,contextDestroyed 以相反顺序调用。
b). javax.servlet.ServletRequestListener实现的requestInitialized以声明时顺序调用,requestDestroyed方法以相反顺序调用。
c). javax.servlet.http.HttpSessionListener实现的sessionCreated方法以声明时顺序调用,sessionDestroyed 方法以相反顺序调用。
d). 未指定其他所有Listener接口的调用顺序。

3.如果使用web.xml中引入的enabled元素禁用了servlet,那么为该servlet指定的url-pattern将不可用。

4.当解析web.xml、web-fragment.xml 和注解之间发生冲突时web应用的web.xml具有最高优先级。

5.如果没有在描述符中指定metadata-complete或在部署描述符中设置为false,通过组合出现在注解和描述符中的元数据导出有效的元数据。合并的规则具体如下:

1). 在web fragment中的配置设置用于扩充那些已指定在主web.xml的配置设置,使用这种方式就好像它们指定在同一个web.xml。
2). 添加到主web.xml的web fragment中的配置设置的顺序由8.2.2节”web.xml和web-fragment.xml顺序”指定。
3). 当主web.xml的metadata-complete 属性设置为true,被认为是完整的且在部署时不会扫描注解和fragment。如果有absolute-ordering和ordering元素将被忽略。当fragment上设置为true时,metadata-complete属性仅适用于在特定的jar包中扫描注解。
4). 除非metadata-complete 设置为true,否则web fragment被合并到主web.xml。合并发生在相关fragment的注解处理之后。
5). 当使用web fragment扩充web.xml时以下被认为配置冲突:

a). 多个<init-param>元素使用相同的<param-name>但不同的<param-value>
b). 多个<mime-mapping>元素使用相同的<extension>但不同的<mime-type>

6). 上面的配置冲突按照如下方式解析:

a). 在主web.xml和web fragment之间的配置冲突被解析为在web.xml的配置具有高优先级。
b). 在两个web fragment之间的配置冲突,冲突的中心元素没有出现在主web.xml,将导致一个错误。必须记录一个有用的消息,且应用必须部署失败。

7). 上面的冲突被解析后,将应用下面这些额外的规则:

a). 可以在多个web-frament中声明任意多次元素并生成到web.xml。比如,<context-param>元素可以以不同的<param-name>添加。
b). 如果指定在web.xml中的覆盖了指定在web-fragment中的同名的值,则可以声明任意多次元素。
c). 如果是最少出现零次且最多出现一次的元素存在于web fragment,且没有在主web.xml中,则主web.xml继承web fragment的设置。如果元素出现在主web.xml和web fragment,则主web.xml的配置设置具有高优先级。例如,如果在主web.xml和web fragment中都声明了相同的servlet,且声明在web fragment中的servlet指定了<load-on-startup>元素,且没在主web.xml指定,则web fragment的<load-on-startup>元素将被使用并合并到web.xml。
d). 如果是最少出现零次且最多出现一次的元素指定在两个web fragment中,且没有出现在主web.xml,则认为是错误的。例如,如果两个web fragment声明了相同的Servlet,但具有不同的<load-on-startup>元素,且相同的Servlet也声明在主web.xml中,但没有<load-on-startup>,则必须报告一个错误。
e).<welcome-file>声明是可添加的。
f). 具有相同<servlet-name><servlet-mapping>元素可以添加到多个web-fragment。在web.xml中指定的<servlet-mapping>覆盖在web-fragment中指定的同名的<servlet-name><servlet-mapping>
g). 具有相同<filter-name><filter-mapping>元素可以添加到多个web-fragment。在web.xml中指定的<filter-mapping>覆盖在web-fragment中指定的同名的<filter-name><filter-mapping>
h). 具有相同<listener-class>的多个<listener>元素被当作一个<listener>声明。
i). 合并产生的web.xml被认为是<distributable>,仅当所有它的web fragment也被标记为<distributable>
j). webfragment的顶级<icon>和它的孩子元素,<display-name>,和<description>元素被忽略。
k). jsp-property-group是可添加的。当绑定jar文件META-INF/resources目录中的静态资源时,推荐jsp-config元素使用url-pattern,反对使用extension映射。此外,如果存在一个fragment的JSP资源,则应该在一个与fragment同名的子目录中。这有助于防止一个web-fragment的jsp-property-group受到来自应用的主docroot中的JSP的影响和受到来自一个fragment的META-INF/resources的JSP的影响。

8). 对于所有资源引用元素 (env-entry, ejb-ref,ejb-local-ref, service-ref, resource-ref, resource-env-ref, message-destination-ref,persistence-context-ref和persistence-unit-ref) 适用如下规则:

a). 如果任意资源引用元素出现在web fragment中,而主web.xml中没有,那么主web.xml继承web fragment的值。如果该元素同时出现在主web.xml和web fragment,使用相同的名字,web.xml具有高优先级。所有fragment的子元素除下面指定的injection-target外其他都被合并到主web.xml中。例如,如果主web.xml和web fragment都使用相同的<resource-ref-name>声明一个<resource-ref>,将使用web.xml中的<resource-ref>且不会合并fragment中的任何子元素除下面声明的<injection-target>外。
b). 如果资源引用元素指定在两个fragment中,而没有指定在主web.xml中,且资源引用元素的所有属性和子元素都是一样的,资源引用将被合并到主web.xml。如果使用相同名字在两个fragment中指定资源引用元素,而没有在web.xml中指定,且属性和子元素是不一样的,那么被认为是错误的。必须报告错误且应用必须部署失败。例如,如果两个web fragment使用相同的<resource-ref-name>声明了<resource-ref>但类型一个指定为javax.sql.DataSource,而另一个指定为一个JavaMail资源,这是错误的且应用必须部署失败。
c). 对于在fragment中使用相同名称的<injection-target>的资源引用元素将被合并到主web.xml。

9). 除了上面定义的web-fragment.xml的合并规则之外,下面的规则适用于使用资源引用注解(@Resource,@Resources, @EJB, @EJBs, @WebServiceRef, @WebServiceRefs, @PersistenceContext,@PersistenceContexts,@PersistenceUnit, 和@PersistenceUnits)。

如果资源引用注解应用到类上,这等价于定义一个资源,但是这不等价于定义一个injection-target。在这种情况下上述规则适用于injection-target元素。

如果在字段上使用资源引用注解,这等价于在web.xml定义injection-target元素。但是如果在描述符中没有injection-target元素,那么fragment中的injection-target仍将被合并到上面定义的web.xml中。

如果从另一方面来说,在主web.xml中有一个injection-target并同时有一个相同资源名的资源引用注解,那么这被认为是对资源引用注解的覆盖。在这种情况下,由于在描述符中指定了一个injection-target,上述定义的规则将适用并覆盖资源引用注解的值。

10). 如果在两个fragment中指定了data-source元素,而没有出现在主web.xml中,且data-source元素的所有属性和子元素都是一样的,data-source将被合并到主web.xml中。如果在两个fragment中指定同名的data-source元素,而没有出现在主web.xml中且两个fragment的属性和子元素不是一样的,这被认为是错误的。在这种情况下,必须报告一个错误且引用必须部署失败。

下面是一些示例,展示了在不同情况下的结果。
代码示例8-4
web.xml – 没有 resource-ref 定义
Fragment 1
web-fragment.xml

<resource-ref>      <resource-ref-nameresource-ref-name="foo">      ...      <injection-target>         <injection-target-class>              com.foo.Bar.class         </injection-target-class>          <injection-target-name>                  baz          </injection-target-name>     </injection-target>  </resource-ref>

有效的metadata将是

<resource-ref>      <resource-ref-nameresource-ref-name="foo">          ....           <injection-target>         <injection-target-class>              com.foo.Bar.class         </injection-target-class>          <injection-target-name>                  baz          </injection-target-name>     </injection-target>  </resource-ref>

代码示例8-5
web.xml

<resource-ref>      <resource-ref-nameresource-ref-name="foo">      ...  </resource-ref> 

Fragment 1
web-fragment.xml

<resource-ref>      <resource-ref-nameresource-ref-name="foo">      ...      <injection-target>         <injection-target-class>              com.foo.Bar.class         </injection-target-class>          <injection-target-name>                  baz          </injection-target-name>     </injection-target>  </resource-ref>  

Fragment 2
web-fragment.xml

<resource-ref>      <resource-ref-nameresource-ref-name="foo">      ...      <injection-target>         <injection-target-class>              com.foo.Bar2.class         </injection-target-class>          <injection-target-name>                  baz2          </injection-target-name>     </injection-target>  </resource-ref>

有效的metadata将是

<resource-ref>      <resource-ref-nameresource-ref-name="foo">          ....      <injection-target>         <injection-target-class>              com.foo.Bar.class         </injection-target-class>          <injection-target-name>                  baz          </injection-target-name>     </injection-target>           <injection-target>         <injection-target-class>              com.foo.Bar2.class         </injection-target-class>          <injection-target-name>                  baz2          </injection-target-name>     </injection-target>  </resource-ref>

代码示例8-6
web.xml

<resource-ref>      <resource-ref-nameresource-ref-name="foo">         <injection-target>         <injection-target-class>              com.foo.Bar3.class         </injection-target-class>          <injection-target-name>                  baz3          </injection-target-name>      ...  </resource-ref>  

Fragment 1
web-fragment.xml

<resource-ref>      <resource-ref-nameresource-ref-name="foo">      ...      <injection-target>         <injection-target-class>              com.foo.Bar.class         </injection-target-class>          <injection-target-name>                  baz          </injection-target-name>     </injection-target>  </resource-ref>  

Fragment 2
web-fragment.xml

<resource-ref>      <resource-ref-nameresource-ref-name="foo">      ...      <injection-target>         <injection-target-class>              com.foo.Bar2.class         </injection-target-class>          <injection-target-name>                  baz2          </injection-target-name>     </injection-target>  </resource-ref>  

有效的metadata将是

<resource-ref>      <resource-ref-nameresource-ref-name="foo">         <injection-target>         <injection-target-class>              com.foo.Bar3.class         </injection-target-class>          <injection-target-name>                  baz3          </injection-target-name>          <injection-target-class>              com.foo.Bar.class          </injection-target-class>          <injection-target-name>              baz          </injection-target-name>          <injection-target-class>              com.foo.Bar2.class          </injection-target-class>          <injection-target-name>              baz2          </injection-target-name>          </injection-target>          ...  </resource-ref>

Fragment1和2的<injection-target>被合并到主web.xml

11)果主web.xml没有指定任何<post-construct>元素,而web-fragment中指定了<post-construct>,那么fragment中的<post-construct>将被合并到主web.xml。不过如果在主web.xml中至少指定一个<post-construct>元素,那么fragment中的<post-construct>将不被合并。由web.xml的作者负责确保<post-construct>列表是完整的。

12)果主web.xml没有指定任何<pre-destroy>元素,且web-fragment中也指定了<pre-destroy>,那么fragment中的<pre-destroy>元素将被合并到主web.xml。不过如果在主web.xml中至少指定一个<pre-destroy>元素,那么fragment中的<pre-destroy>将不被合并。由web.xml的作者负责确保<pre-destroy>列表是完整的。

13). 在处理完web-fragment.xml之后,在处理下一个fragment之前相应fragment的注解被处理以完成有效的metadata。以下规则用于处理注解:

14). 通过注解指定的metadata,尚未存在于描述符中,将被用来扩充有效的描述符。

a). 指定在主web.xml或web fragment中的配置比通过注解指定的配置具有更高优先级。
b). 使用@WebServlet 注解定义的Servlet,要使用描述符覆盖其值,描述符中的servlet名字必须与使用注解指定的servlet名字匹配(如果没有通过注解指定名字,那么使用明确指定的或默认的名字)。
c). 使用注解定义的Servlet和Filter初始化参数,如果描述符中的初始化参数的名字完全匹配指定在注解中的名字,则将描述符中的覆盖。可在注解和描述符之间添加初始化参数。
d). 以给定servlet名字指定在描述符中的url-pattern,将覆盖注解指定的url pattern。
e). 使用@WebFilter 注解定义的Filter,要使用描述符覆盖其值,描述符中的Filter名字必须与使用注解指定的Filter名字匹配(如果没有通过注解指定名字,那么使用明确指定的或默认的名字)。
f). Filter应用的url-pattern,当以给定Filter名字指定在描述符中时,将覆盖注解指定的url pattern。
g). Filter应用的DispatcherType,当以给定Filter名字指定在描述符中时,将覆盖注解指定的DispatcherType。
h). 下面的例子演示了上面的一些规则:

使用注解声明的Servlet和在打包到相应的web.xml描述符中的Servlet:@WebServlet(urlPatterns="/MyPattern", initParams={@WebInitParam(name="ccc", value="333")})  public class com.acme.Foo extends HttpServlet {    ...  }web.xml <servlet>     <servlet-class>com.acme.Foo</servlet-class>     <servlet-name>Foo</servlet-name>     <init-param>       <param-name>aaa</param-name>       <param-value>111</param-value>     </init-param>  </servlet>  <servlet>     <servlet-class>com.acme.Foo</servlet-class>     <servlet-name>Fum</servlet-name>     <init-param>       <param-name>bbb</param-name>       <param-value>222</param-value>     </init-param>  </servlet>  <servlet-mapping>     <servlet-name>Foo</servlet-name>     <url-pattern>/foo/*</url-pattern>  </servlet-mapping>  <servlet-mapping>     <servlet-name>Fum</servlet-name>     <url-pattern>/fum/*</url-pattern>  </servlet-mapping>  因为使用注解声明的Servlet名字与web.xml中声明的servlet名字不匹配,在web.xml中除了其他的声明外,还添加了一个注解指定的新的servlet声明,相当于:<servlet>     <servlet-class>com.acme.Foo</servlet-class>     <servlet-name>com.acme.Foo</servlet-name>     <init-param>       <param-name>ccc</param-name>       <param-value>333</param-name>     </init-param>  </servlet>  如果上面的web.xml被替换为如下: <servlet>    <servlet-class>com.acme.Foo</servlet-class>    <servlet-name>com.acme.Foo</servlet-name>    <init-param>      <param-name>aaa</param-name>      <param-value>111</param-value>    </init-param>  </servlet>  <servlet-mapping>    <servlet-name>com.acme.Foo</servlet-name>    <url-pattern>/foo/*</url-pattern>  </servlet-mapping>  那么有效的描述符将等价于:<servlet>    <servlet-class>com.acme.Foo</servlet-class>    <servlet-name>com.acme.Foo</servlet-name>    <init-param>      <param-name>aaa</param-name>      <param-value>111</param-value>    </init-param>    <init-param>      <param-name>ccc</param-name>      <param-value>333</param-value>    </init-param>  </servlet>  <servlet-mapping>    <servlet-name>com.acme.Foo</servlet-name>    <url-pattern>/foo/*</url-pattern>  </servlet-mapping>  

8.2.4 共享库 / 运行时可插拔性

除了支持fragment和使用注解的外,另一个要求是我们不仅能plug-in绑定在WEB-INF/lib下的资源,也能够plugin框架共享副本—包括能plug-in到web容器的东西,如建立在web容器之上的JAX-WS、JAX-RS和JSF。ServletContainerInitializer允许处理这样的使用情况,如下所述。

在容器/应用启动时,由容器通过jar services API查找一个ServletContainerInitializer实例。框架提供的ServletContainerInitializer实现必须绑定在jar包的META-INF/services目录中的一个叫做javax.servlet.ServletContainerInitializer的文件中,根据每个jar services API,指定ServletContainerInitializer的实现类。

除ServletContainerInitializer外,我们还有一个注解—HandlesTypes。在ServletContainerInitializer 实现上的HandlesTypes注解用于表示感兴趣的一些类,这些类可能在HandlesTypes的value中指定了注解(类型、方法或字段级别的注解),或者是这些类继承/实现了该类的超类中的某个类。容器使用HandlesTypes注解来决定什么时候调用initializer的onStartup方法。当检测一个应用的类看它们是否匹配ServletContainerInitializer的HandlesTypes注解指定的所有条件时,如果应用的一个或多个可选的JAR包缺失,容器可能遇到类装载问题。由于容器不能决定这些类装载失败的类型是否会阻止应用正常工作,它必须忽略它们,同时也提供一个记录这些问题的配置选项。

如果ServletContainerInitializer实现没有@HandlesTypes注解,或如果没有匹配任何指定的HandlesType,那么它会为每个应用使用null作为Set的值调用一次。这将允许initializer来决定基于应用中可用的资源是否需要初始化一个Servlet/Filter。

在任何Listener的事件被触发之前,当应用正在启动时,ServletContainerInitializer的onStartup方法将被调用。

ServletContainerInitializer的onStartup得到一个类的Set,其或者继承/实现initializer表示感兴趣的类,或者它使用@HandlesTypes注解指定的任意类来注解。

下面一个具体的例子展示了这是如何工作的。

让我们学习JAX-WS web service 运行时。

JAX-WS运行时实现通常不是绑定到每个war包。其实现将绑定一个ServletContainerInitializer的实现(如下所示)且容器将使用services API来查找(绑定在jar包中的META-INF/services目录中的一个叫做javax.servlet.ServletContainerInitializer的文件,它将指出如下所示的JAXWSServletContainerInitializer)。

@HandlesTypes(WebService.class)  JAXWSServletContainerInitializer implements ServletContainerInitializer  {      public void onStartup(Set<Class<?>> c, ServletContext ctx)  throws ServletException {          // 这里使用JAX-WS 特定的代码来初始化运行库和设置mapping等。          ServletRegistration reg = ctx.addServlet("JAXWSServlet", "com.sun.webservice.JAXWSServlet");          reg.addServletMapping("/foo");      }  }  

框架的jar包也可能被绑定到war文件中的WEB-INF/lib目录。如果ServletContainerInitializer被绑定到应用的WEB-INF/lib目录内的一个JAR包中,它的onStartup方法在绑定到的应用启动期间仅被调用一次。换句话说,如果ServletContainerInitialzer被绑定到WEB-INF/lib目录外的一个JAR包中,但仍能被运行时的服务提供商查找机制发现,每次启动应用时都会调用它的onStartup方法。

ServletContainerInitializer接口的实现将被运行时的服务查找机制或语义上与它等价的容器特定机制发现。无论是哪种情况,必须忽略从绝对顺序中排除的web fragment JAR包中的ServletContainerInitializer服务,这些服务被发现的顺序必须遵照应用的类装载委托模型。

8.3 JSP容器可插拔性

ServletContainerInitializer和编程注册功能使在Servlet和JSP容器之间提供一个清晰的职责分离成为可能,通过由Servlet容器只负责解析web.xml和web-fragment.xml资源,而把解析标签库描述符(TLD)资源委托给JSP容器。

在此之前,web容器必须扫描TLD资源的所有监听器声明。Servlet3.0以后,该职责可以委托给JSP容器。JSP容器是内嵌到一个兼容Servlet3.0的Servlet容器中,可以提供它自己的ServletContainerInitializer实现,搜索传递到它的onStartup方法的ServletContext参数的所有TLD资源,扫描这些资源的监听器声明,并向ServletContext注册相应的监听器。

另外,Servlet3.0之前,JSP容器通常必须扫描应用的部署描述符中所有与jsp-config相关的配置。Servlet3.0以后,Servlet容器必须可通过ServletContext.getJspConfigDescriptor方法获取到应用程序的web.xml和web-fragment.xml部署描述符中的所有与jsp-config有关的配置。

在TLD中发现的和编程注册的所有ServletContextListener提供的功能有限。任何试图调用一个在Servlet3.0中添加的ServletContext API方法将导致一个UnsupportedOperationException。

另外,兼容Servlet3.0的Servlet容器必须提供一个名字为javax.servlet.context.orderedLibs的ServletContext属性,它的值(java.util.List

8.4 处理注解和fragment

Web应用可同时包括注解和web.xml/web-fragment.xml部署描述符。如果没有部署描述符,或有一个但其metadata-complete没有设置为true,如果应用程序中使用了web.xml、web-fragment和注解则必须处理。下表描述了是否处理注解和web.xml的fragment。
这里写图片描述

0 0