前言
本文档是关于Java™ Servlet4.0版本的规范的。本文描述了Java Servlet API的标准。
概述
1.1 什么是Servlet?
Servlet是一种基于Java™的web组件,它是由容器管理的,能够生成动态内容。类似于其他基于Java™的组件,servlet是平台独立的Java类,它被编译成平台无关的字节码,能够被动态加载进基于Java技术的web服务器中运行。容器有时候也被叫做servlet引擎,是一种提供了servlet功能的web服务器扩展。Servlet通过servlet容器实现的request/response与web客户端进行交互。
1.2 什么是Servlet容器?
Servlet容器是web服务器或web应用服务器的一部分,它通过发送请求和响应来提供网络服务,解码基于MIME的请求,格式化基于MIME的响应。Servlet容器包含及管理servlet的整个生命周期。
Servlet容器能够被内置进宿主机的web服务器中,或者通过使用web服务器的本地扩展API作为附加组件安装到web服务器中。Servlet容器也可以被内置或安装到基于web的应用服务器中。
所有servlet容器必须支持HTTP作为请求及响应的协议,其他基于请求及响应的协议例如HTTPS也应该被支持。容器必须实现的HTTP规范的版本是HTTP/1.1及HTTP2. 当支持HTTP/2时,servlet容器必须支持“h2"及"h2c"协议标识符。这就意味着所有servlet容器必须支持ALPN。因为servlet容器可以有缓存机制,在分发到servlet前容器可能对来自客户端的请求进行修改,或者不进行分发直接响应请求。
Servlet容器可以在servlet执行的环境中设置安全限制。在java平台,标准版本(JavaSE),或者JavaEE环境中,这些限制使用java平台中的安全架构进行设计。例如,一些应用服务器会限制线程的创建以确保容器的其他组件不会受负面影响。
在当前java平台下,Java8是构建servlet容器的最低版本。
1.3 例子
以下是一系列典型的事件:
- 客户端(web浏览器)访问web服务器,生成HTTP请求
- web服务器接收请求并把请求转交给servlet容器;servlet容器可以跟web服务器运行在同一个进程中,也可以在同一个宿主机的不同进程中,或者跟web服务器不同的宿主机上。
- servlet容器根据其servlet配置决定调用哪个servlet,通过请求对象和响应对象调用servlet。
- servlet使用request对象识别出远程用户是谁,HTTP post参数及其他相关数据作为request请求的一部分被发送到servlet。servlet执行已经编程好的相关业务逻辑,之后生成数据返回给客户端。servlet通过reponse对象给client返回数据。
- 一旦servlet完成了对request的处理,servlet容器要确保响应被正确的输出,之后将控制权返还给web服务器
1.4 Servlets跟其他技术的比较
在功能上,servlet提供了比CGI更高层次的抽象,但是低于web框架(JavaServer Faces)的抽象。
与其他服务器端扩展机制相比,servlet具有以下优势:
- 因为使用了不同的进程模型,通常要比CGI脚本要快,
- 使用了标准的API,这些标准API被很多web服务器支持
- 具有Java语言的所有优点,包括易于开发和平台独立性
- 能够访问JAVA平台的大量API
1.5 与JavaEE平台的关系
Java Servlet API 4.0版本是JavaEE8里面必备API。为能在JavaEE环境中能运行,Servlet容器及部署到其中的Servlet必须满足JavaEE规范中描述的要求。
1.6 与Java Servlet规范2.5版本的兼容性
在Servlet2.5中,meata-complete只会在部署时影响注解的扫描。Servlet2.5中不存在web-fragment(web片段)的概念。然而在Servlet3.0中及以后的版本,metadata-complete影响在部署配置中指定的及web片段部署时所有注解的扫描。这个描述的版本必须不能影响容器扫描web应用程序的注解。规范的特定版本实现必须扫描配置中能支持的所有注解,除非metadata-complet被指定了
Servlet接口
Servlet接口是Java Servlet API中的核心抽象。所有的Servlet通过直接实现这个接口,或者更通用的做法,通过继承一个类来实现这个接口。Java Servlet API中实现servlet接口的类有两个:GenericServlet及HttpServlet。大部分场景下,开发者通过继承HttpServlet来实现自己的servlet。
2.1 请求处理方法
基础的Servlet接口定义了一个service方法来处理客户端请求。每一个被servlet容器路由到一个具体servlet实例的请求,都会调用这个方法。
web应用程序并发请求的处理通常需要web开发者将servlet的service方法设计为可以多线程执行。
通常情况下,web容器在不同线程中并行执行servlet的service方法来处理并发请求。
2.1.1 HTTP特定请求处理方法
HttpServlet抽象子类在基础servlet接口上添加了额外的方法,HttpServlet类处理基于HTTP的请求时,这些额外的方法通过调用service方法来自动调用执行。
- doGet 处理HTTP GET请求
- doPost 处理HTTP POST请求
- doPut 处理HTTP PUT请求
- doDelete 处理HTTP DELETE请求
- doHead 处理HTTP HEAD请求
- doOptions 处理HTTP OPTIONS请求
- doTrace 处理HTTP TRACE请求
通常情况下,开发基于HTTP的Servlet时,Servlet开发者只需要关注doGet和doPost方法即可。其他方法一般是面向非常熟悉HTTP编程的开发者使用。
2.1.2 额外的方法
doPut和doDelete方法允许Servlet开发者使用这些特性支持HTTP/1.1客户端。HttpServlet中的doHead方法是doGet方法的特殊形式,它只返回由doGet方法产生的head信息。doOptions方法返回servlet可以支持哪些HTTP方法。doTrace方法生成包含发送trace请求中的所有head的实例。
2.1.3 有条件的GET支持
HttpServlet接口定义了getLastModified方法来支持有条件的GET操作。一个带条件的GET操作只有在指定的时间之后被修改的情况下才会请求相应的资源。在某些情形下,这种方法的使用有助于降低网络资源的负载。
2.2 实例的数量
Servlet声明既可以通过第8章“注解及可插入性”中所描述的注解来声明,也可以通过作为web应用程序中部署描述符的一部分来声明,通过该声明控制servlet容器提供的servlet的实例数。
对于未部署在分布式环境下的servlet(默认情况下),servlet容器对于每一个servlet声明只能使用一个实例。然而,对于实现了SingleThreadModel接口的servlet,servlet容器可以实例化多个实例用于处理繁重的request请求及将请求序列化到某个特定实例。
备注:SingleThreadModel接口已经被弃用
在这种情况下,servlet以应用描述符作为发布物部署时,每个JVM中一个容器里一个servlet声明只有一个实例。然而如果servlet在发布的应用程序中实现了SingleThreadModel接口,在容器的每个JVM中容器可以实例化多个servlet的实例。
2.2.1 注意单线程模型
使用SingleThreadModel接口确保在servlet实例的service方法中一次只有一个线程在执行。需要特别注意的是这种保证只适用于这个servlet实例,因为容器可能有选择对这些实例对象进行池化。对于那些每次都可以被多个servlet实例访问的对象,例如HttpSession实例对象,在特定的时刻对于多个servlet是可用,包括那些实现了SingleThreadModel的servlet实例。
建议开发人员使用其他方式解决该问题,而不是实现该接口,例如避免实例对象的使用或者访问资源时同步代码。SingleThreadModel接口在本版本的规范中已经被废弃。
2.3 Servlet生命周期
Servelt是定义了明确的生命周期管理,该生命周期定义了如何加载,如何实例化,初始化,处理来在客户端的请求,已经如何停止服务。这个生命周期在API中由javax.servlet的init,service,destroy方法来表示。所有Servlet必须直接实现Servlet接口或者通过继承GenericServlet或HttpServlet间接实现Servlet接口。
2.3.1 加载和实例化
Servelt容器负责加载及实例化servlet。加载和实例化发生在容器启动时,或者延迟直到容器探测到需要servlet去服务请求时。
当Servlet引擎启动时,需要的servlet class必须被servlet容器定位到。servlet容器使用通用的Java类加载机制加载这些servlet class。加载可以来自本地文件系统,远程文件系统,或其他网络服务。
在加载完Servlet类后,容器对其进行实例化以供使用。
2.3.2 实例化
在实例化Servlet对象之后,容器在servlet处理来自客户端的请求前完成其初始化。Servlet在初始化时读取一些持久化的配置数据,初始化耗时的资源(例如基于JDBC API的连接),以及一些其他一次性的活动。容器使用一个唯一的实现了ServletConfig接口的对象去调用servlet的init方法完成完成servlet的实例化。配置对象运行servlet访问web应用配置信息中的一些键值对参数。配置对象也可以让servlet访问描述servlet运行环境的对象(实现了ServletContext接口)。参考第4章"Servlet Context",获取更多关于ServletContext接口的信息。
2.3.2.1 初始化时的错误条件
在初始化国产,servlet实例可能会抛出UnavailableException或者ServletException。在这种情况下,这种servlet不能放入到激活服务中,并且需要被servlet容器释放。destroy方法没有被调用视作没有初始化成功。初始化失败后,容器可以实例化及初始化一个新的实例。此规则的例外情况时,UnavailableException标识最短的不可用时间,容器必须等待该时间段后再去创建及实例化一个新的servlet实例。
2.3.2.2 注意事项
当工具加载和内省Web应用程序时,静态初始化方法的触发跟调用init方法不同。在servelt接口的init方法被调用前,开发者不应该假设一个servlet已经处于激活容器中。例如,当静态(class)的初始化方法被调用时,servlet不应该尝试建立与数据库的连接或者Enterprise JavaBeans容器的连接。
2.3.3 请求处理
Serlvet被正确初始化后,servlet容器可以使用它去处理客户端请求。请求由ServletRequest类型的请求对象标识。servelt通过调用提供了ServletResponse类型对象的方法来填充对请求的响应。这些对象作为参数传递给servlet接口的service方法。在HTTP请求的情况下,容器提供的对象类型为HttpServletRequest及HttpServletResponse。请注意,由servlet容器提供服务的servlet实例可能在其生命周期中都不处理任何请求。