Servlet是Server Applet的简称,指服务器端小程序或服务连接器,主要功能在于交互式地浏览和修改数据,生成动态Web内容。目前,最新的Servlet规范版本为Servlet 4.0(JSR 369)。
Java Servlet是运行在Web服务器或应用服务器上的程序,是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器的数据库或应用程序之间的中间层。
使用Servlet可以收集来自网页表单的用户输入,呈现来自数据库或其他源的记录,还可以动态创建网页。
图2-1所示为Servlet的整体架构。
图2-1 Servlet架构
Servlet主要执行以下任务。
(1)读取客户端(浏览器)发送的显式数据,包括网页上的HTML表单,或是自定义的HTTP客户端程序的表单。
(2)读取客户端(浏览器)发送的隐式HTTP请求数据,包括Cookies、媒体类型和浏览器能理解的压缩格式等。
(3)处理数据并生成结果。这个过程可能需要访问数据库,执行RMI等远程过程调用,调用Web服务,或者直接计算得出对应的响应。
(4)发送显式数据(文档)到客户端(浏览器),数据格式可以是多种多样的,包括文本文件(HTML、XML或JSON)、二进制文件(GIF图像)、Excel表格等。
(5)发送隐式HTTP响应到客户端(浏览器),设置Cookies和缓存参数,以及其他类似的任务。
Servlet生命周期可被定义为从创建Servlet到其被销毁的整个过程,以下是Servlet的生命周期遵循的过程。
(1)Servlet调用init方法进行初始化。
(2)Servlet调用service方法来处理客户端的请求。
(3)Servlet通过调用destroy方法终止。
(4)最后,Servlet由JVM的垃圾回收器进行回收。
下面详细讨论生命周期中的方法。
1.init方法
init方法被设计成只调用一次。其在第一次创建Servlet时被调用,在后续每次用户请求时不再调用。因此,它用于一次性初始化。
Servlet创建于用户第一次调用对应该Servlet的URL时,也可以指定Servlet在服务器第一次启动时被加载。
当用户调用一个Servlet时,就会创建一个Servlet实例,每一个用户请求都会产生一个新的线程,在适当的时候移交给doGet方法或doPost方法。init方法简单地创建或加载一些数据,这些数据将被用于Servlet的整个生命周期。
init方法的定义如下。
2.service方法
service方法是执行实际任务的主要方法。Servlet容器调用service方法处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个Servlet请求时,都会产生一个新的线程并调用服务。service方法用于检查HTTP请求类型(GET、POST、PUT、DELETE等)。
下面是该方法的特征。
service方法由容器调用,它会在适当的时候调用doGet、doPost、doPut、doDelete等方法。所以不用对service方法做任何动作,只需要根据客户端的请求类型重写doGet方法、doPost方法。
doGet方法和doPost方法是每次服务请求中最常用的方法。
3.doGet方法
当Servlet容器接收到GET请求时,会将该请求交由doGet方法处理。处理逻辑写在重写的doGet方法中,代码如下。
4.doPost方法
当Servlet容器接收到POST请求时,会将该请求交由doPost方法处理。处理逻辑写在重写的doPost方法中,代码如下。
5.destroy方法
当Servlet容器确定Servlet应该从服务中移除时,将调用Servlet接口的destroy方法,以允许Servlet释放它自己使用的任何资源和保存任何持久化的状态。例如,当想要节省内存资源或Servlet被关闭时,Servlet容器可以执行destroy方法。
在Servlet容器调用destroy方法之前,必须保证正在执行service方法的线程已经执行完毕,或者超过了服务器定义的时间限制。
一旦调用了Servlet实例的destroy方法,容器就无法再路由其他请求到该Servlet实例了。如果容器需要再次使用该Servlet,则必须用该Servlet类的一个新实例。在destroy方法执行完毕后,Servlet容器必须释放Servlet实例以便被回收。
destroy方法的定义如下。
基本的Servlet接口定义了service方法用于处理客户端的请求。当有请求到达时,该方法由Servlet容器路由到一个Servlet实例来调用。
Web应用的并发请求处理通常需要Web开发人员去设计适合多线程执行的Servlet,从而保证Service方法能在一个特定时间点处理多线程并发执行。通常Web容器对于并发请求会使用同一个Servlet处理,并且在不同的线程中并发执行Service方法。
HttpServlet抽象子类在基本的Servlet之上添加了一些协议相关的方法,并且这些方法能根据HTTP请求类型自动将HttpServlet中实现的service方法转发到相应协议的处理方法上。这些方法如下。
(1)doGet处理HTTP GET请求。
(2)doPost处理HTTP POST请求。
(3)doPut处理HTTP PUT请求。
(4)doDelete处理HTTP DELETE请求。
(5)doHead处理HTTP HEAD请求。
(6)doOptions处理HTTP OPTIONS请求。
(7)doTrace处理HTTP TRACE请求。
一般情况下,开发基于HTTP的Servlet时,Servlet开发人员只需实现doGet和doPost请求处理方法。如果开发人员想使用其他处理方法,那么使用方式跟之前类似,即HTTP编程都类似。
doPut方法和doDelete方法允许Servlet开发人员让支持HTTP/1.1的客户端使用这些功能。HttpServlet中的doHead方法可以认为是doGet方法的一种特殊形式,它仅返回由doGet方法产生的header信息。doOptions方法返回当前Servlet支持的HTTP方法。doTrace方法返回的响应包含TRACE请求的所有头信息。
Servlet容器是Web服务器或应用服务器的一部分,用于提供基于请求/响应发送模式的网络服务,解码基于MIME的请求,以及格式化基于MIME的响应。Servlet容器同时也包含并管理其生命周期中的Servlet。
Servlet容器可以嵌入宿主的Web服务器中,或者通过Web服务器的本地扩展API单独作为附加组件安装。Servlet容器也可以内嵌或安装到启用Web功能的应用服务器中。
所有的Servlet容器都必须支持HTTP协议以处理请求和响应,但额外的基于请求/响应的协议,如HTTPS(HTTP over SSL)的支持是可选的。对于HTTP规范要求的版本,容器必须支持HTTP/1.1和HTTP/2。
在支持HTTP/2时,Servlet容器必须支持“h2”“h2c”协议标识符,这意味着所有Servlet容器都必须支持ALPN。因为容器可能有缓存,可以在将协议标识符传递给Servlet之前修改来自客户机的请求,也可以在将Servlet发送到客户机之前修改响应,或者可以响应请求而不将其传递给Servlet。
Java SE 8是与Servlet 4.0一起使用的最低Java平台版本。
常见的Servlet容器有闭源的也有开源的,包括Tomcat、Jetty、Oracle Application Server、Oracle Weblogic Server、JBoss Application Server等。其中,Tomcat、Jetty在开源界比较有名,且在市场上占有率比较高。
下面就Tomcat和Jetty的异同点进行比较。
1.相同点
Tomcat和Jetty都是Servlet引擎,它们都支持标准的Servlet规范和Java EE规范;它们都是开源的,可以免费使用。
2.不同点
(1)在架构上,Jetty的架构比Tomcat的架构更为简单,具体如下。
①Jetty的架构是基于Handler实现的,主要的扩展功能都可以用Handler来实现,扩展简单。
②Tomcat的架构是基于容器设计的,进行扩展时需要了解Tomcat的整体设计结构,不易扩展。
(2)在性能上,Jetty和Tomcat差异不大,具体如下。
①Jetty可以同时处理大量连接且可以长时间保持连接,适合于Web聊天应用等。
②Jetty的架构简单,因此作为服务器,Jetty可以按需加载组件,减少不需要的组件,能减少服务器的内存开销,从而提高服务器性能。
③Jetty默认采用NIO结束,在处理I/O请求上更占优势,在处理静态资源时性能较高。
④Tomcat适合处理少数非常繁忙的连接,也就是说连接生命周期短的话,Tomcat的总体性能更高。
⑤Tomcat默认采用BIO处理I/O请求,在处理静态资源时性能较差。
(3)在其他方面,二者差异如下。
①Jetty的应用更加快速,修改简单,对新的Servlet规范支持较好。
②Tomcat目前应用比较广泛,对Java EE和Servlet的支持更加全面,很多特性可直接集成进来。
3.总结
Jetty的主要特性包括易用性、可扩展性及易嵌入性。可以把Jetty理解为一个嵌入式的Web服务器,Jetty的运行速度较快,而且是轻量级的。Jetty的轻量级也使其在处理高并发、细粒度请求的场景下显得更快速、高效。
Jetty更灵活,体现在其可插拔性和可扩展性,更易于开发者对Jetty本身进行二次开发,定制一个符合自身需求的Web服务器。
Tomcat支持的规范更全面,功能也更多,也显得更加“重量级”。所以,当面临大规模企业级应用时,Jetty需要扩展,在这种场景下,使用Tomcat则会更加方便。
Jetty更满足公有云的分布式环境的需求,而Tomcat更符合企业级环境。
综上所述,Lite框架基于Jetty实现内嵌容器。在本书的后续章节中,还会对Jetty进行深入探讨。要了解更多有关Servlet的内容,可以参阅笔者所著的开源电子书《Java Servlet 3.1规范》。