8. 注解和可插拔性

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 类。

下面是如何使用该注解的一个示例。

@WebServlet(”/foo”)

public class CalculatorServlet extends HttpServlet{

//...

}

下面是如何使用该注解指定更多的属性的一个示例。

@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。

下面是如何使用该注解的一个示例。

@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

javax.servlet.http.HttpSessionIdListener

示例:

@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 属性和的元素被解析为一个绝对路径且默认为 javax.servlet.context.tempdir。如果指定了相对地址,它将是相对于 tempdir 位置。绝对路径与相对地址的测试必须使用 java.io.File.isAbsolute。

8.1.6 其他注解/惯例

除了这些注解,定义在第 15-183 页 15.5 节的“注解和资源注入”将继续工作在这些新注解上下文中。

默认情况下,所有应用将有 index.htm 和 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 文件。

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

welcome

WelcomeServlet

RequestListener

以上的 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 类型的顶级元素,且在一个 web-fragment.xml中仅能有一个元素。如果存在一个元素,它必须考虑用于 artifact 顺序(除非出现重复名异常,如上文所述)。

两种情况必须被考虑,以允许应用程序配置资源来表达它们的顺序配置。

绝对顺序:在 web.xml 中的元素。在一个 web.xml 中仅能有一个元素。

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

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

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

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

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

匹配请求的过滤器链的顺序是它们在 web.xml 中声明的顺序。

Servlet 在请求处理时实例化或在部署时立即实例化。在后一种情况,以它们的 load-on-startup 元素表示的顺序实例化。load-on-startup 大于 0 时 Servlet 会在部署时立即实例化,顺序由大到小,小于 0 时,在请求处理时实例化。

在之前发布的规范,上下文 Listener 以随机顺序调用。在 Servlet3.0, Listener 以它们在 web.xml 中声明的顺序调用,初始化方法按声明顺序调用,销毁方法按声明逆序调用。

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

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

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

除 ServletContainerInitializer 外,我们还有一个注解——HandlesTypes。在 ServletContainerInitializer 实现上的 HandlesTypes 注解用于表示感兴趣的一些类,它们可能指定了 HandlesTypes 的 value 中的注解(类型、方法或自动级别的注解),或者是其类型的超类继承/实现了这些类之一。 无论是否设置了 metadata-complete,HandlesTypes 注解将应用。

当检测一个应用的类看是否它们匹配 ServletContainerInitializer 的HandlesTypes 指定的条件时,如果应用的一个或多个可选的 JAR 包缺失,容器可能遇到类装载问题。由于容器不能决定是否这些类型的类装载失败

将阻止应用正常工作,它必须忽略它们,同时也提供一个将记录它们的配置选项。

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

在任何 Servlet 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> 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类型)包含了由 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

java 可插拔注解_20200311 8. 注解和可插拔性相关推荐

  1. java高级语言特性_Java语言高级特性——注解

    一 注解作用或意义 定义 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制. 注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据.注解对它们注 ...

  2. 简述java中的注释以及用法_怎样理解 Java 注解和运用注解编程?

    正好最近在公众号(BetterAndroid)发了一篇关于注解的文章,贴在这里吧,希望对题主有帮助. 一.什么是注解 我们都知道在Java代码中使用注释是为了提升代码的可读性,也就是说,注释是给人看的 ...

  3. java 继承 注解_在java中实现组合注解原理分析(注解继承)

    今天在自定义注解的时候,原计划实现一个类似于Spring中的注解@Component的功能,如果稍有留意一下,会发现,在Spring中我们常见的注解,其实都继承了@Component注解:如下图所示: ...

  4. Java注解和xml_Spring注解配置和xml配置优缺点比较

    Spring注解配置和xml配置优缺点比较 编辑 ​ 在昨天发布的文章<spring boot基于注解方式配置datasource>一文中凯哥简单的对xml配置和注解配置进行了比较.然后朋 ...

  5. java 反射 注解 运用_Java注解与反射的使用

    打开 Eclipse,新建 Java 项目"注解与反射",在 src 下右键并建立包 "注解与反射",在包下右键并建立 Annotation (注解)文件,名称 ...

  6. java注解机制_Java 注解机制

    一.注解中的信息已经在Class中了,我们应该如何读取出来 1 java.lang.reflect.AnnotatedElement接口:2 3 publicAnnotation[] getAnnot ...

  7. java ee中javamail注解_JavaEE之注解

    1注解:Annotation注解,是一种代码级别的说明.它是JDK1.5及以后版本引入的一个特性,与类.接口.枚举是在同一个层次,给计算机,JVM提供解读信息的. 2注解的作用:编译检查:代码分析,编 ...

  8. 深入理解Java注解Annotation之注解处理器

    如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了.使用注解的过程中,很重要的一部分就是创建于使用注解处理器.Java SE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处 ...

  9. 【Android APT】注解处理器 ( 根据注解生成 Java 代码 )

    文章目录 一.生成 Java 代码 二.实现 IButterKnife 接口 三.视图绑定主要操作 四.完整注解处理器代码 五.博客资源 Android APT 学习进阶路径 : 推荐按照顺序阅读 , ...

最新文章

  1. Ubuntu16.04下配置最新Vs Code的C/C++开发环境
  2. C# 3.0 入门系列(一)
  3. C++ 文件大小格式化
  4. 【DBMS 数据库管理系统】数据仓库中 数据追加 ( 时标方法 | DELTA 文件法 | 前后映像文件法 | 日志文件法 )
  5. Python初学的几个迷惑点
  6. 解决ant design vue中的modal弹框样式修改无效问题 修改modal样式无效
  7. 【OpenCV入门教程之二】 一览众山小:OpenCV 2.4.8 or OpenCV 2.4.9组件结构全解析(转)...
  8. c/c++的预处理定义 Stringizing Operator (#) Charizing Operator (#@) Token-Pasting Operator (##)
  9. 哈希算法——论文整理(未完)
  10. BIM族库下载——Revit配景族
  11. Duplicate Net Names Wire Net......
  12. pon终端测试仪_6304-PON终端测试仪报价_测试仪-北京海富达科技有限公司
  13. plotm matlab,MATLAB画地图的工具:worldmap和m_map
  14. 墨画子卿第三章第4节:躺着修行
  15. Android开发艺术探索 第一章 Activity的生命周期和启动模式
  16. Neural Summarization by Extracting Sentences and Words
  17. 正则表达式贪婪性---点星(.*) 20190618
  18. java中文转拼音_java中文转拼音
  19. oracle转换人民币,月光软件站 - 编程文档 - 数据库 - 如何在ORACLE中實現人民幣大寫的轉換...
  20. 浏览器历史大事记和JavaScript的诞生

热门文章

  1. 什么是Protocol Buffers / protobuf / protobuffer?一种服务器和客户端的消息交互方式
  2. 用Cairo画IBM logo并输出为pdf,ps,svg格式文件
  3. 为什么工程师要掌握FPGA开发知识?
  4. [GitHub]一个简单的网络驱动
  5. UNIX网络编程:unpv13e编译错误:net/if_dl.h:没有那个文件或目录
  6. 笔记本vm系统的分辨率不好调整_苹果笔记本电脑怎么设置使用今声优盒
  7. Python项目实践:国家财政数据趋势演算
  8. springboot多环境切换
  9. 在matlab中安装命令窗口,安装完后发现命令窗口有这个?怎么回事?
  10. mysql源码目录在哪_Mysql源码学习——源码目录结构