Servlet3.1规范翻译 - 注解和可插拔性
https://github.com/javahongxi
本文转载自kaitao.toutiao.im
注解和可插拔性
本章讨论Servlet 3.0规范定义的注解和使web应用内使用的框架和库能够可插拔的增强。
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。
以下注解必须被Servlet 3.0兼容的容器支持。
8.1.1 @WebServlet
该注解用于在Web应用中定义Servlet组件。该注解在一个类上指定并包含声明Servlet的元数据。必须指定注解的urlPatterns或value属性。所有其他属性是可选的默认设置(请参考javadoc获取更多细节)。当注解上唯一属性是url模式时推荐使用value且当也有使用其他属性时使用urlPatterns属性。在同一注解上同时使用value 和urlPatterns属性是非法的。如果没有指定Servlet名字则默认是全限定类名。被注解的sevlet必须指定至少一个url模式进行部署。如果同一个Servlet类以不同的名字声明在部署描述符中,必须实例化一个新的Servlet实例。如果同一个Servlet类使用定义在4-35页的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 res) {
- ...
- }
- }
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-183页15.5节的“注解和资源注入”将继续工作在这些新注解上下文中。
默认情况下,所有应用将有index.htm(l)和index.jsp在welcome-file-list列表中。该描述符可以用来覆盖这些默认设置。
当使用注解时,从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描述符指定。
为了给开发人员更好的可插拔性和更少的配置,在这个版本(Servlet 3.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应用的一个逻辑分区,以这样一种方式,在应用中使用的框架可以定义所有制品(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。
在部署期间,容器负责扫描上面指定的位置和发现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>内,runtime必须确保任何web-fragment未明确指定在<absolute-ordering>部分的以处理的顺序包括在这一点上。
■ 如果web-fragment.xml 文件没有<ordering>或web.xml 没有<absolute-ordering>元素,则artifact被假定没有任何顺序依赖。
■ 如果runtime发现循环引用,必须记录提供有用信息的消息,应用必须部署失败。此外, 用户采取的一系列动作可能是在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, <no id>, C, D
■ B, E, F, <no id>, D, C
■ E, B, F, <no id>, C, D
■ E, B, F, <no id>, D, C
■ E, B, F, D, <no id>, C
■ B, E, F, D, <no id>, 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元素,如果存在。
a. 匹配请求的过滤器链的顺序是它们在web.xml中声明的顺序。
b. Servlet在请求处理时实例化或在部署时立即实例化。在后一种情况,以它们的load-on-startup 元素表示的顺序实例化。
c. 在之前发布的规范,上下文Listener以随机顺序调用。在Servlet3.0,Listener以它们在web.xml中声明的顺序调用,如下所示:
i. javax.servlet.ServletContextListener实现的contextInitialized方法以声明时顺序调用,contextDestroyed 以相反顺序调用。
ii. javax.servlet.ServletRequestListener 实现的requestInitialized 以声明时顺序调用,requestDestroyed 方法以相反顺序调用。
iii. javax.servlet.http.HttpSessionListener 实现的sessionCreated方法以声明时顺序调用,sessionDestroyed 方法以相反顺序调用。
iv. 其他任何Listener接口的调用顺序是未指定的。
3. 如果在web.xml使用enabled元素禁用引入的servlet,那么该servlet将不可以指定url-pattern。
4. 当在web.xml、web-fragment.xml 和 注解之间解析发生冲突时web应用的web.xml具有最高优先级。
5. 如果没有在描述符中指定metadata-complete 或在部署描述符中设置为false,通过组合出现在注解和描述符中的metadata导出有效的metadata。合并的规则具体如下:
a. 在web fragment中的配置设置用于扩充那些已指定在主web.xml的配置设置,使用这种方式就好像它们指定在同一个web.xml。
b. 添加到主web.xml的web fragment中的配置设置的顺序由8-70页的8.2.2节“web.xml和web-fragment.xml顺序”指定。
c. 当主web.xml的metadata-complete 属性设置为true,被认为是完整的且在部署时不会扫描注解和fragment。如果有absolute-ordering和ordering元素将被忽略。当设置fragment上的为true时,metadata-complete属性仅适用于在特定的jar包中扫描注解。
d. 除非metadata-complete 设置为true,否则web fragment被合并到主web.xml。合并发生在相关fragment的注解处理之后。
e. 当使用web fragment扩充web.xml时以下被认为配置冲突:
i. 多个<init-param>元素使用相同的<param-name>但不同的<param-value>
ii. 多个<mime-mapping>元素使用相同的<extension>但不同的<mime-type>
f. 上面的配置冲突被解析为如下:
i. 在主web.xml和web fragment之间的配置冲突被解析为在web.xml的配置具有高优先级。
ii. 在两个web fragment之间的配置冲突,冲突的中心元素没有出现在主web.xml,将导致一个错误。必须记录一个有用的消息,且应用必须部署失败。
g. 上面的冲突被解析后,这些额外的规则适用:
i. 可以在多个web-frament中声明任意多次元素并生成到web.xml。比如,<context-param>元素可以以不同的名字添加。
ii. 如果指定在web.xml中的覆盖了指定在web-fragment中的同名的值,则可以声明任意多次元素。
iii. 如果是最少出现零次且最多出现一次的元素存在于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。
iv. 如果是最少出现零次且最多出现一次的元素指定在两个web fragment,且没有出现在主web.xml,则认为是错误的。例如,如果两个web fragment声明了相同的Servlet,但具有不同的<load-on-startup>元素,且相同的Servlet也声明在主web.xml,但没有<load-on-startup>,则必须报告一个错误。
v. <welcome-file>声明是可添加的。
vi. 具有相同<servlet-name>的<servlet-mapping>元素可以添加到多个web-fragment。在web.xml中指定的<servlet-mapping>覆盖在web-fragment中指定的同名的<servlet-name>的<servlet-mapping>。
vii. 具有相同<filter-name>的<filter-mapping>元素可以添加到多个web-fragment。在web.xml中指定的<filter-mapping>覆盖在web-fragment中指定的同名的<filter-name>的<filter-mapping>。
viii. 具有相同<listener-class>的多个<listener>元素被当作一个<listener>声明。
ix. 合并产生的web.xml被认为是<distributable>,仅当所有它的web fragment也被标记为<distributable>。
x. web fragment的*<icon>和它的孩子元素,<display-name>,和<description>元素被忽略。
xi. 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的影响。
h. 对于所有资源引用元素 (env-entry, ejb-ref, ejb-local-ref, service-ref, resource-ref, resource-env-ref, message-destination-ref, persistence-context-ref and persistence-unit-ref) 如下规则适用:
i. 如果任意资源引用元素出现在web fragment,主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>。
ii. 如果资源引用元素指定在两个fragment,当没有指定在主web.xml中,且资源引用元素的所有属性和子元素都是一样的,资源引用将被合并到主web.xml。如果使用相同名字在两个fragment中指定资源引用元素,且没有在web.xml中指定,属性和子元素是不一样的,那么被认为是错误的。错误必须被报告且应用必须部署失败。例如,如果两个web fragment使用相同的<resource-ref-name>声明了<resource-ref>但类型一个指定为javax.sql.DataSource另一个指定为JavaMail,这是错误的且应用必须部署失败。
iii. 对于在fragment中使用相同名称的<injection-target> 的资源引用元素将被合并到主web.xml。
i. 除了上面定义的web-fragment.xml的合并规则之外,下面的规则适用于使用资源引用注解(@Resource, @Resources, @EJB, @EJBs, @WebServiceRef, @WebServiceRefs, @PersistenceContext, @PersistenceContexts,@PersistenceUnit, and @PersistenceUnits)。
如果资源引用注解应用到类上,这等价于定义了一个资源,但是这不等价于定义一个injection-target。在这种情况下上述规则适用于injection-target元素。
如果在字段上使用资源引用注解,这等价于在web.xml定义injection-target元素。但是如果在描述符中没有injection-target元素,那么fragment中的injection-target仍将被合并到上面定义的web.xml。
如果从另一方面来说,在主web.xml中有一个injection-target并同时有一个同资源名的资源引用注解,那么这被认为是对资源引用注解的覆盖。在这种情况下,由于在描述符中指定了一个injection-target,上述定义的规则将适用于除了覆盖的资源引用注解。
j. 如果在两个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-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-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-name="foo">
- ...
- </resource-ref>
Fragment 1
web-fragment.xml
- <resource-ref>
- <resource-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-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-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-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-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-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-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
k. 如果主web.xml没有指定任何<post-construct>元素,且web-fragment中也指定了<post-construct> ,那么fragment中的<post-construct>将被合并到主web.xml。不过如果在主web.xml中至少指定一个<post-construct>元素,那么fragment中的<post-construct>将不被合并。由web.xml的作者负责确保<post-construct>列表是完成的。
l. 如果主web.xml没有指定任何<pre-destroy>元素,且web-fragment中也指定了<pre-destroy>,那么fragment中的<pre-destroy>元素将被合并到主web.xml。不过如果在主web.xml中至少指定一个<pre-destroy>元素,那么fragment中的<pre-destroy>将不被合并。由web.xml的作者负责确保<pre-destroy>列表是完成的。
m. 在处理完web-fragment.xml之后,在处理下一个fragment之前相应fragment的注解被处理以完成有效的metadata。以下规则用于处理注解:
n. 通过注解指定的metadata,尚未存在于描述符中,将被用来扩充有效的描述符。
i. 指定在主web.xml或web fragment中的配置比通过注解指定的配置具有更高优先级。
ii. 使用@WebServlet 注解定义Servlet,要使用描述符覆盖其值,描述符中的servlet名字必须匹配使用注解指定的servlet名字(明确指定或如果注解没有指定则是默认名字)。
iii. 使用注解定义的Servlet和Filter初始化参数,如果描述符中的初始化参数的名字完全匹配指定在注解中的名字,则将被描述符中的覆盖。初始化参数在注解和描述符之间是可添加的。
iv. url-pattern,当以给定servlet名字指定在描述符中时,将覆盖注解指定的url pattern。
v. 使用@WebFilter 注解定义的Filter,要使用描述符覆盖其值,描述符中的Filter名字必须匹配使用注解指定的Filter名字(明确指定或如果注解没有指定则是默认名字)。
vi. Filter应用的url-pattern,当以给定Filter名字指定在描述符中时,将覆盖注解指定的url pattern。
vii. Filter应用的DispatcherType,当以给定Filter名字指定在描述符中时,将覆盖注解指定的DispatcherType。
viii. 下面的例子演示了上面的一些规则:
使用注解声明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>
- </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容器之上的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 值的集合调用一次。这将允许initializer基于应用中可用的资源决定是否需要初始化Servlet/Filter。
在任何Listener的事件被触发之前,当应用正在启动时,ServletContainerInitializer的onStartup方法将被调用。
ServletContainerInitializer’s的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 服务被排除于一个absolute ordering必须被忽略,这些服务被发现的顺序必须遵照应用的类装载委托模型。
8.3 JSP容器可插拔性
ServletContainerInitializer和编程式注册特性可以在Servlet和JSP容器之间提供一个清晰的职责分离,通过由Servlet容器只负责解析web.xml和web-fragment.xml资源,而解析标签库描述符(TLD)资源委托给JSP容器。
在此之前,web容器必须扫描TLD资源寻找任何Listener声明。使用Servlet3.0后,该职责可以委托给JSP容器。JSP容器是内嵌到一个Servlet3.0兼容的Servlet容器中,可以提供它自己的ServletContainerInitializer实现,搜索传递到它的onStartup方法的ServletContext参数寻找任何TLD资源,扫描这些资源寻找Listener声明,并向ServletContext注册相关的Listener。
另外,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<java.lang.String>类型)包含了由ServletContext所代表的应用的WEB-INF/lib目录中的JAR文件的名字列表,按照它们的web fragment名字的排序(可能排除如果fragment JAR包已经被排除在absolute-ordering),或者null如果应用没有指定任意绝对或相对顺序。
8.4 处理注解和fragment
Web应用可同时包括注解和web.xml/web-fragment.xml部署描述符。如果没有部署描述符,或有一个但其metadata-complete没有设置为true,web.xml、web-fragment和注解如果在应用中使用则必须被处理。下表描述了是否处理注解和web.xml的fragment。
表 8-1 注解和web fragment处理要求
部署描述符 |
metadata-complete |
处理注解和web fragment |
web.xml 2.5 |
yes |
no |
web.xml 2.5 |
no |
yes |
web.xml 3.0 |
yes |
no |
web.xml 3.0 |
no |
yes |