Web 应用
Web应用是Web服务器上组成一个完整应用的 servlets,HTML pages,classes 和其他资源的一个集合。Web应用可以被捆绑并运行在不同供应商的不同容器内。
10.1 Web服务器中的Web应用
Web服务器里的Web应用以一个特定的路径为根。例如,catalog应用可能定位到http://www.mycorp.com/catalog。所有以此前缀为开始的请求会被路由到代表该 catalog 应用的 ServletContext。servlet 容器可以创建 Web应用的自动生成规则。例如 ~user/ 映射可以用来映射一个基于/home/user/public_html的 Web应用。默认情况下,在任何同一时间一个Web应用的实例必须运行在一个VM上。如果应用通过其部署描述符将其标记为“distributable(分布的)”,此行为可以被覆盖。标记为分布的应用必须遵守比普通Web应用更严格的规则。本规范中设置了这些规则。
10.2 与ServletContext的关系
servelt容器必须强制Web应用与 ServletContext 之间一一对应的。ServletContext 对象提供给 servlet 它的应用视图。
10.3 Web应用的元素
Web应用可以由以下项目组成:
▪ Servlets
▪JSP Pages1
▪工具类
▪静态文档(HTML,图片,声音,等等)
▪客户端Java applets,beans,和类
▪将以上元素绑在一起的描述性元信息
10.4 部署层次
为部署和打包目的,本规范定义了一个可以存在于开放文件系统,归档文件或其它格式的层次结构。建议 servlet 容器支持该结构作为运行时表现形式,但不是必须的。
10.5 目录结构
Web 应用作为一个结构化层级目录存在。此结构层级的根作为应用中文件的文档根。例如,Web容器中以 /catalog 作为上下文路径的应用,Web应用层级根目录下的 index.html 和 在WEB-INF/lib 下的JAR文件中/META-INF/resources 目录下包含的index.html 都满足来自 /catalog/index.html 的请求。如果 index.html 同时存在于根上下文和 应用 WEB-INF/lib 目录里的JAR文件 /META-INF/resources中,则必须使用根上下文中的index.html。匹配 URLs 到上下文路径的规则在12章”映射请求到Servlets” 中给出。由于应用的上下文路径决定了Web应用内容的 URL 命名空间,Web容器必须拒绝Web应用在此 URL 命名空间中定义可能造成潜在冲突的上下文路径。比如尝试部署相同上下文路径的另外一个Web应用,就会发生冲突。由于请求匹配资源使用大小写敏感的方式,潜在冲突的确定也必须以大小写敏感的方式进行。
在应用层级结构中存在一个名为”WEB-INF”的特殊目录。此目录包含与应用相关的不在应用根文档中的所有东西。大部分 WEB-INF 节点不是应用公共文档树的一部分。除了打包在位于 WEB-INF/lib 中的 JAR 文件中 META-INF/resources 的静态资源和JSP,包含在 WEB-INF 目录中的其他文件不能被容器提供给客户端。但是 WEB-INF 目录的内容对调用 ServletContext 的 getResource 和 getResourceAsStream 方法的 servlet 代码是可见的,并且可以使用 RequestDispatcher 调用被公开。因此,如果应用开发者需要从 servlet 代码访问他不想直接对Web客户端直接公开的应用特殊配置信息,可以将它放到该目录下。由于请求匹配资源映射使用大小写敏感的方式,例如客户端请求,‘/WEB-INF/foo’, ‘/WEb-iNf/foo’,不应该将位于 /WEB-INF 下的Web应用的内容返回,包括任何形式的目录列表。
WEB-INF 目录的内容:
▪ /WEB-INF/web.xml 部署描述符。
▪ /WEB-INF/classes/ servlet和工具类的目录。此目录中的类必须对应用的类加载器可用。
▪ /WEB-INF/lib/*.jar Java 归档文件区。这些文件包括打包在JAR文件中的 servlets,benas,静态资源和JSPs 和其他对Web应用有用的工具类。Web应用类加载器必须能从任何这些归档文件中加载类。Web 应用必须首先从 WEB-INF/classes 目录加载类,然后从 WEB-INF/lib 目录下的库 JARs加载。另外,任何来自客户端访问 WEB-INF/ 目录里资源的请求,除了静态资源被打包进JAR文件的情况外,必须返回一个SC_NOT_FOUND(404)响应。
10.5.1 应用目录结构示例
下面是一个示例Web应用中的所有文件列表:
/index.html
/howto.jsp
/feedback.jsp
/images/banner.gif
/images/jumping.gif
/WEB-INF/web.xml
/WEB-INF/lib/jspbean.jar
/WEB-INF/lib/catalog.jar!/META-INF/resources/catalog/moreOffers/books.html
/WEB-INF/classes/com/mycorp/servlets/MyServlet.class
/WEB-INF/classes/com/mycorp/util/MyUtils.class
10.6 Web 应用归档文件
使用标准Java归档工具,Web应用可以被打包并签入一个Web归档格式(WAR)文件。例如,一个用来“issue tracking(跟踪问题)”的应用可以发布在一个名为 issuetrack.war 的归档文件中。
当打包成这种格式时,将生成一个包含对Java归档工具有用信息的 META-INF 目录。此目录不能被容器作为内容直接提供给作为Web客户端请求的响应,虽然它的内容对通过调用 ServletContext 的 getResource 和 getResourceAsStream 的 servlet 代码是可见的。另外对任何访问 META-INF 目录下资源的请求必须返回 SC_NOT_FOUND(404) 响应。
10.7 Web 应用部署描述符
Web应用描述符(见14章:部署描述符)包括以下配置类型和部署信息:
▪ ServletContext 初始化参数
▪ Session 配置
▪ Servlet/JSP 定义
▪ Servlet/JSP 映射
▪ MIME Type 映射
▪ Welcome File 列表
▪ Error Pages
▪ Security 安全
10.7.1 扩展依赖
当大量应用使用相同的代码或资源时,他们通常会被作为库文件安装进容器里。这些文件经常是通用和标准的API,可以在不牺牲移植性的情况下使用。仅被一个或少数应用使用的文件将作为Web应用的一部分来访问。容器必须为这些库提供一个目录。存放在这个目录的文件必须可以跨Web应用可用。这个目录的位置是容器指定的。servlet容器用来加载这些库文件的类加载器必须与在同一JVM中的所有Web应用的类加载器相同。此类加载器实例必须位于Web应用类加载器的父加载器链中。为了保持移植性,应用开发者需要知道Web容器上安装了什么扩展,并且容器需要知道 WAR 中的 servlets 依赖哪些库。依赖这些扩展的应用开发者必须在 WAR 文件中提供一个 META-INF/MANIFEST.MF 条目列出WAR需要的所有扩展。这个 manifest 条目的格式必须遵循标准的 JAR manifest 格式。在 Web 应用的部署中,Web 容器必须使扩展的正确版本对[遵守“可选包版本识别”机制定义规则]的应用可用。Web容器必须能识别 WAR 包中 WEB-INF/lib 目录下的任何 JAR 库中 manifest 条目里声明的依赖。如果Web容器不能满足以这种方式声明的依赖,它应该使用一个有意义的错误信息拒绝应用。
10.7.2 Web应用类加载器
容器用来加载 WAR 中 servlet 的类加载器必须允许开发者使用 getResource 来加载[遵循正常 Java SE 词法的] WAR 中 JAR 库中包含的任何资源。在 Java EE 许可协议描述中,servlet 容器不是 Java EE 产品的一部分,不应该允许应用去覆盖 Java SE 平台类,比如 java.* 和 javax.* 中的类,Java SE 不允许被修改。容器不应该允许应用去覆盖或者访问容器的实现类。建议应用的类加载器实现为:加载打包进WAR中的类和资源优先于加载容器范围内的位于JAR库中的类和资源。实现必须保证为容器中部署的每一个 web 应用调用 Thread.currentThread.getContextClassLoader() 必须返回一个实现了在本节中指定约定的 ClassLoader 实例。另外,每个部署的 web 应用的 ClassLoader 实例必须是一个独立的实例。在让任何回调(包括 listener 回调)进入 web 应用之前,容器必须按照上面的描述设置线程上下文 ClassLoader(?),一旦回调返回,将它设置回原来的 ClassLoader。
10.8 替换 Web 应用
服务器应该能在不重启容器的情况下使用新版本替换应用。当应用被替换时,容器应该提供一个稳健的方法保留应用中的会话(session)数据。
10.9 错误处理
10.9.1 请求属性
当错误发生时,Web 应用必须能指定应用中用来提供错误响应内容体的其它资源。这些资源的规范在部署描述符中设置。
如果错误处理的位置(the location of the error handler)是一个 servlet 或 一个 JSP 页面:
▪ 被容器创建的初始未包装的请求和响应对象被传递给 servlet 或 JSP 页面。
▪ 请求路径和属性被设置为:对错误资源的RequestDispatcher.forward 已经被执行一样。(?)
▪ 表10-1中的请求属性必须被设置:
表10-1 请求属性和他们的类型
请求属性 类型
javax.servlet.error.status_code java.lang.Integer
javax.servlet.error.exception_type java.lang.Class
javax.servlet.error.message java.lang.String
javax.servlet.error.exception java.lang.Throwable
javax.servlet.error.request_uri java.lang.String
javax.servlet.error.servlet_name java.lang.String
这些属性允许 servlet 根据状态码,异常类型,错误信息,传播的异常对象,错误产生在其中的 servlet 处理的请求URI(由 getRequestURI 调用确定)和错误产生的 servlet 的逻辑名来生成专门内容。
随着本规范 2.3 版本中属性列表中异常对象的引入,异常类型和异常信息属性变得多余。保留它们是为了向后兼容API的早期版本。
10.9.2 错误页面
为了允许开发者自定义 servlet 产生错误时返回 Web 客户端的内容的外观(呈现),部署描述符定义了一系列错误页面说明。 当 servlet 或 filter 为特定状态码调用 response 的 sendError 或如果 servlet 产生的异常或错误传播到了该容器时,此语法允许容器返回资源的配置。
如果 response 的 sendError 方法被调用,容器查询Web应用的错误页面声明,使用状态码语法并尝试匹配。如果有一个匹配,容器返回此位置(<location>)条目指示的资源。
容器或filter在处理请求期间可能会抛出下面的异常:
▪ runtime exceptions or errors 运行时异常或错误
▪ ServletExceptions 或 它的子类
▪ IOExceptions or 或它的子类
Web应用可以使用 exception-type 元素声明错误页面。在此情况下,容器通过比较抛出的异常和使用 exception-type 元素定义的错误页面(<error-page>)定义列表来匹配异常类型。匹配导致容器返回在位置(<location>)条目指示的资源。在类继承层次最近的匹配胜出。
如果使用类层次匹配没有包含合适的 exception-type 的 error-page 声明,并且抛出的异常是 ServletException 或它的子类,容器使用 ServletException.getRootCause 方法提出被包装的异常。使用被包装的异常(ServletException 或其子类中包装的),再次传入错误页面声明,重新尝试匹配错误页面声明。
在部署描述符中使用 exception-type 元素的错误页面(<error-page>)声明中 exception-type 的类名必须是唯一的。同样,使用 status-code 元素的 error-page 声明在部署描述符中 status code 也必须是唯一的。
如果部署描述符中的一个 error-page 元素没有包含 exception-type 或 error-code 元素,此错误页面是默认错误页面。
当使用 RequestDispatcher 或 filter.doFilter 方法调用发生错误时,错误页面机制不会干预。在此种方式下,filter 或者 servlet 有机会使用 RequestDispatcher 处理产生的错误。(这段不知道对不对)
如果 servlet 产生的错误没有被上面介绍的错误页面机制处理,容器必须确保发送带有状态码 500 的响应。
默认 servlet 和 container 将使用 sendError 方法发送 4XX 和 5XX 状态响应,以便调用错误机制。默认 servlet 和 container 将使用 sendError 方法发送 2XX 和 3XX 状态响应,不会调用错误机制。
如果应用(程序)使用在2.3.3.3节描述的异步操作,2-10页的“异步处理”,应用程序负责处理应用程序创建的线程上的错误。容器【可以】处理通过 AsyncContext.start 创建的线程产生的错误。参看2-16页的几节关于处理 AsyncContext.dispatch 期间产生的错误,“容器必须如下捕获并处理 dispatch 方法执行期间可能产生的任何错误和异常”。
10.9.3 错误过滤器
错误页面机制作用在容器产生的原始未包装/未过滤的请求和响应对象上。此机制在 6.2.5 节描述,“过滤器和 RequestDispatcher”,在错误响应产生之前可以用来指定应用的过滤器。
10.10 欢迎文件
Web应用开发者可以在Web应用部署描述符中定义称为欢迎文件的[部分URI]有序列表。Web应用部署描述符 schema 中描述了此列表的部署描述符语法。
此机制的目的是允许开发者为容器指定一个 部分的URIs 有序列表,当有对应 WAR 中一个目录条目的 URI 的请求没有映射到 Web 组件时,用来追加到这些 URI之后。这种类型的请求就是有名的 有效部分请求(a valid partial request)。
这种用法的好处通过下面的例子来搞清楚: 可以定义一个 ‘index.html’ 的欢迎文件,以便对像 URL host:port/webapp/directory/ 的请求,’directory’ 是 WAR 里一个没有映射到 servlet 或 JSP page 的条目,返回给客户端的是 ‘host:port/webapp/directory/index.html’。
如果Web容器接收到一个有效部分请求,Web容器必须检查定义在部署描述符中的欢迎文件。欢迎文件列表是一个没有前导和后导 / 的有顺序的部分URL列表。Web 服务器必须按在部署描述符中指定的顺序逐个添加欢迎文件到部分请求,并且检查在WAR是否有一个静态资源映射到请求的URI。如果没有找到匹配,Web 服务器必须重新按照部署描述符中指定的顺序依次添加每个欢迎文件到部分请求并检查是否有一个 servlet 映射到请求URI。Web 容器必须将请求发送到 WAR中匹配的第一个资源。容器可以使用 forward,redirect 或容器指定的机制将请求发送到欢迎资源,与直接请求没有什么区别。
按照上面描述的方式如果没有匹配的欢迎文件,容器可以按照它能找到的合适方式来处理请求。对于某些配置可能意味着返回目录列表,其它配置可能返回 404 响应。
考虑这样的一个 Web 应用:
■ 部署描述符列出了下面的欢迎文件。
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
■ WAR中的静态文件如下:
/foo/index.html
/foo/default.jsp
/foo/orderform.html
/foo/home.gif
/catalog/default.jsp
/catalog/products/shop.jsp
/catalog/products/register.jsp
▪ 请求URI /foo 将会重定向到 URI /foo/。
▪ 请求URI /foo/ 将返回 /foo/index.html 。
▪ 请求URI /catalog 将会重定向到 URI /catalog/。
▪ 请求URI /catalog/ 将会返回 /catalog/default.jsp 。
▪ 请求URI /catalog/index.html 将会导致 404 NOT FOUND.
▪ 请求URI /catalog/products 将会重定向到 URI /catalog/products/。
▪ 对URI /catalog/products/ 的请求将会传递到“默认”servlet, 如果有的话。如果没有“默认”servlet 映射,此请求可能导致 404 not found , 可能导致包括 shop.jsp 和 register.jsp 的目录列表,或者可能导致容器定义的其他行为。参见 12.2 节,“映射规范”中“默认” servlet 的定义。
▪ 上面列出的所有静态内容也可以与上面打包进 jar 文件 META-INF/resources 目录里的内容一起打包进 JAR 文件。此 JAR 文件可以包含在 Web 应用的 WEB-INF/lib 目录里。
10.11 Web应用环境
Servlet 容器不是 Java EE 技术标准的一部分,鼓励实现但不必须,在 15.2.2 节“Web应用环境和Java EE规范”中描述了实现应用环境功能。如果它们(容器)没有实现所需功能来支持[在上面部署应用所依赖]的环境,容器应该提供一个警告。
10.12 Web应用部署
当web应用被部署进容器,在web 应用开始处理客户端的请求之前,必须按照下面的步骤依次执行:
▪ 实例化在部署描述符中通过<listener>元素标识的每个事件 listener 一个实例。
▪ 为每个实现 ServletContextListener 的已实例化 listener 实例,调用 contextInitialized() 方法。
▪ 实例化在部署描述符中通过 <filter> 元素标识的每个 filter 的一个实例,然后调用每个 filter 实例的 init() 方法。
▪ 包含<load-on-startup> 元素的 <servlet> 元素标识的 servlet,按照 load-on- startup 元素值定义的(按由小到大)顺序,每个 servlet 实例化一个实例,并且调用每个 servlet 的 init() 方法。
10.13 包含web.xml部署描述符
如果web应用不包含任何 Servlet,Filter 或 Listener 组件,或者使用注解声明了相同(就是使用了注解),不需要包含一个 web.xml 文件。换句话说,一个仅包括静态文件或JSP页面的应用不需要一个web.xml 来介绍。