第3章?Servlet基础
本章要点:
* 能知道Servlet的历史发展和特点。
* 能理解Servlet的生命周期和运行过程。
* 会编写Servlet代码。
* 会使用Servlet开发Web网站。
* 会部署Servlet到Web服务器Tomcat。
?3.1
Servlet概述
在Servlet出现之前,Web服务器通过CGI技术与客户端进行交互。具体实现时,需要使用Perl、Shell Script或C语言编写CGI程序,再部署到Web服务器,从而使Web服务器能处理来自客户端网页表单的输入信息并将处理后的信息反馈给客户端。但是,CGI技术存在很多缺点,主要如下。
* CGI程序开发比较困难。要求开发人员具备处理参数传递的能力,需要开发经验。
* CGI程序不可移植。为某一特定平台编写的CGI程序只能运行于该特定平台中。
* CGI程序内存和CPU开销大,而且在同一进程中不能服务多个客户。因为每个 CGI程序存在于一个由客户端请求激活的Web服务器进程中,并且在请求完成后被卸载。
随着Java语言的广泛使用,Servlet迅速成为动态网站的主要开发技术。实际上,Servlet可以定义为一种基于Java技术的Web组件,用来扩展以请求和响应为模型的Web服务的能力。它是一种独立于平台和协议的服务器端的Java应用程序,与传统的从命令行启动的Java应用程序不同,Servlet本身没有main方法,不是由用户或开发人员调用的,而是由另外一个应用程序——Servlet容器调用和管理的。其中,Servlet容器又称为引擎,承担着运行环境的作用,管理和维护所有Servlet的完整生命周期,并将所有的Servlet编译成字节码从而生成动态的内容以供Web请求调用。
本书使用Tomcat作为运行Java Web网站的服务器,也就是说,Tomcat是Servlet容器,也是Servlet的运行环境。具体运行时,Tomcat负责把客户端的请求传递给Servlet,并把处理结果返回给客户端。当多个客户端请求同一个Servlet时,Tomcat将处理这些并发问题并负责为每个客户端启动一个线程,之后,这些线程的运行和销毁由Tomcat负责。
相比于CGI技术,Servlet具有以下特点。
* 高效。在服务器上仅有一个Java 虚拟机在运行,其优势在于当多个来自客户端的请求访问时,Servlet为每个请求分配一个线程,而不是进程。
* 方便。Servlet提供了大量的实用程序,如处理复杂的HTML表单数据、读取和设置HTTP头,以及处理Cookie和跟踪会话等。
* 跨平台。Servlet用Java语言编写,可以在不同操作系统平台和不同应用服务器平台运行。
* 功能强大。在Servlet中,许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如,Servlet能够直接和Web服务器交互,而CGI程序一般不能实现。Servlet还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。
* 灵活性和可扩展性强。采用Servlet开发的Web应用具备Java的面向对象特性,因此应用灵活,可扩展性强。
?3.2
Servlet的生命周期与运行过程
Servlet运行在Web服务器的Servlet容器里,容器根据Servlet的规范,执行Servlet对象的初始化、运行和卸载等动作。Servlet在容器中从创建、初始化、执行到卸载的过程被称为Servlet的生命周期。
(1)当客户端第一次请求Servlet时,容器加载开发人员编写的HttpServlet类。在Java Web网站开发过程中,开发人员编写Servlet代码一般直接继承javax.servlet.http包中的HttpServlet类。
(2)Servlet容器根据客户端请求创建HttpServlet对象实例,并把这些实例加入HttpServlet实例池中。
(3)Servlet容器调用HttpServlet的初始化方法init,完成初始化。
(4)Servlet容器创建一个HttpServletRequest对象实例和一个HttpServletResponse对象实例。HttpServletRequest对象实例封装了客户端的HTTP请求信息,而HttpServletResponse对象实例是一个新实例。
(5)Servlet容器把HttpServletRequest对象实例和HttpServletResponse对象实例传递给HttpServlet的执行方法service。service方法可被多次请求调用,各调用过程运行在不同的线程中,互不干扰。
(6)HttpServlet对象实例在执行service方法时,通常从HttpServletRequest对象读取HTTP请求数据,访问来自HttpSession或Cookie对象的状态信息,执行特定应用的处理并且用HttpServletResponse对象生成HTTP响应数据。
(7)当Servlet容器关闭时会自动调用HttpServlet对象实例的destroy方法,释放运行时占用的资源。
从图3-1中可以看出,当多个客户端请求同一个Servlet时,Servlet容器为每个客户请求启动一个线程。init方法只在Servlet第一次被请求加载时调用一次,当该Servlet再次被客户端请求时,Servlet容器将启动一个新的线程,在该线程中调用service方法响应客户端请求。在Servlet生命周期的各个阶段中,执行service方法时是真正处理业务的阶段,所以开发人员在编写Servlet相关代码时,主要在这个方法中编写代码实现业务功能。
图3-1 Servlet的生命周期与运行过程
?3.3
开发第一个Servlet
开发编写Servlet类的方式是继承javax.servlet.http包中的HttpServlet类,应在service方法中编写代码响应请求。但是HttpServlet类中的service方法已经实现,部分源代码如下。
源程序:HttpServlet类中service方法的部分代码
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) { //若为Get请求
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_POST)) { //若为Post请求
doPost(req, resp);
} else { //若为其他请求
…
}
}
从上述源代码中可知,service方法根据请求的类型Get、Post等,调用相应的方法doGet、doPost等,所以开发人员实际编写Servlet类时,只要实现doGet、doPost等方法即可。
实例3-1 第一个Servlet程序
本实例利用Servlet的doGet方法实现在页面上输出节能环保的提示信息。
源程序:FirstServlet.java
package com.example.ch03;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/FirstServlet")
public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("
");
out.println("节能环保tips:空调设置温度:" +
"夏季不得低于26℃;冬季不得高于20℃。
");
out.println("");
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
操作步骤:
(1)在Book项目中创建ch03模块。右击Book项目名称,在弹出的快捷菜单中选择New→Module命令,在弹出的对话框中选择模块类型为Java Enterprise,设置模块名称为ch03、位置为D:\ideaProj\Book\ch03,选择模块结构为Web application,选择应用服务器为Tomcat 9.0.29,然后单击Next按钮,在弹出的对话框中单击Finish按钮,ch03模块建立完成。
(2)右击com.example. ch03包,在弹出的快捷菜单中选择New→Servlet命令,然后输入名称FirstServlet,按Enter键确认建立文件,项目文件结构如图3-2所示。
图3-2 项目文件的结构图
(3)在FirstServlet.java文件中输入源代码。
(4)单击运行工具条,选择Edit Configurations,如图3-3所示。
图3-3 选择配置Tomcat
(5)在Run/Debug Configurations对话框中选择Deployment选项卡,单击“+”按钮,选择Artifact选项,在下拉列表中选择ch03:war exploded,单击OK按钮,最后修改Application context为“/ch03”。单击OK按钮,完成ch03模块网站在Tomcat中的部署。配置界面如图3-4所示。
图3-4 部署ch03模块网站
(6)单击运行工具栏中的“运行”按钮,启动Tomcat。Tomcat在启动时,IDEA界面中将输出Tomcat启动和部署当前项目的相关信息,如图3-5所示。其中①表示Tomcat耗时32ms完成启动;②表示当前项目已经成功部署到Tomcat中。
图3-5 Tomcat启动时的提示信息
(7)在浏览器地址栏中输入网址http://localhost:8080/ch03/FirstServlet,查看运行效果,如图3-6所示。
图3-6 FirstServlet.java的运行效果
程序说明:
* “response.setContentType("text/html");”表示响应内容格式为HTML文本。
* “response.setCharacterEncoding("UTF-8");”表示响应内容语言编码字符集为UTF-8。
* 在Servlet直接输出HTML页面信息,必须执行上述两行代码。
* “操作步骤”中的(4)(5)两步,仅需要在ch03模块网站第一次运行时设置。
?3.4
Servlet的部署方法
Servlet容器是Servlet的运行环境,管理和维护Servlet的完整生命周期,所以Servlet必须要部署到Servlet容器中,才能运行起来处理客户端请求。Servlet的部署方法有两种:一种是通过web.xml部署;另一种是通过注解部署。
* 3.4.1 通过web.xml部署Servlet
在IDEA开发工具中创建Web Application项目时,会自动创建webapp和WEB-INF文件夹。webapp是本项目Web站点的根节点,用于存放html、jsp、css、js、jpg等类型资源文件,WEB-INF中包含Web站点的配置文件web.xml。
实例3-2 通过web.xml部署Servlet
本实例利用web.xml文件设置Servlet的配置信息。
源程序:DeployServlet.java
package com.example.ch03;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class DeployServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println("");
out.println("节能环保tips:节能减排靠大家;低碳生活一同享。
");
out.println("");
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
源程序:web.xml文件代码
DeployServlet
com.example.ch03.DeployServlet
count
100
5
DeployServlet
/DeployServlet
操作步骤:
(1)右击com.example.ch03包,在弹出的快捷菜单中选择New→Servlet命令,然后输入名称DeployServlet,按Enter键确认建立文件。
(2)在DeployServlet.java文件中输入源代码,务必删除@WebServlet所在行的代码。
(3)在webapp\WEB-INF\web.xml文件中输入源代码。
(4)单击运行工具栏中的“运行”按钮,启动Tomcat。
(5)在浏览器地址栏中输入网址http://localhost:8080/ch03/DeployServlet,查看运行效果,如图3-7所示。
图3-7 实例3-2的运行效果
程序说明:
* 删除@WebServlet所在行的代码,是因为开发工具创建的Servlet文件默认包含注解,而本实例是通过web.xml部署Servlet,所以需要删除注解。
* 标记。XML文件中必须有一个根标记,web.xml的根标记是。
* 标记。可以配置多个不同名称的标记。标记中有四个子标记:①子标记的内容是Tomcat创建Servlet对象的名字,不能重名;②子标记的内容是指定Tomcat用什么类来创建Servlet对象;③子标记的内容是正整数或者0时,表示Tomcat在启动时就加载并初始化这个Servlet,值越小就越先被加载,如果不指定或者为负整数则在客户端第一次请求时再加载;④子标记内容是参数初始值,在Servlet的init方法内通过getInitParameter方法读取。
* 标记。需要与标记成对使用。中有两个子标记:①子标记必须与对应标记中的内容相同;②子标记用来指定客户端请求Servlet模式,即访问Servlet的URL。例如,若子标记的内容是“/SecondServlet”,那么客户端浏览器请求访问时在地址栏中输入的URL内容应为http://localhost:8080/ch03/SecondServlet。
* 3.4.2 通过注解方式部署Servlet
Servlet 3.0版本包含注解(Annotation)新特性,提供了更加便利的部署方式,简化了开发流程。本书中采用的开发环境为JDK8与Tomcat9,支持Servlet 4.0版本,所以在IDEA开发工具中新建的Servlet默认使用注解方式部署Servlet。本书后续的实例都采用注解方式部署Servlet。
@WebServlet注解将继承于javax.servlet.http.HttpServlet的类标注为可以处理用户请求的Servlet。Tomcat根据注解的具体属性配置将相应的类部署为Servlet。该注解具有表3-1给出的常用属性。表中的属性均为可选属性,但是 value属性或者urlPatterns属性是必需的,且二者不能共存,如果同时指定则忽略 value属性。
表3-1 注解@WebServlet的属性
属?性?名
类 ? 型
描 述
name
String
Servlet的名称,等价于。没有显式指定则默认值为全类名(包含包名和类名)
urlPatterns
String[]
访问Servlet的URL,指定一组 Servlet的URL。等价于标签
value
String[]
访问Servlet的URL,等价于urlPatterns属性
loadOnStartup
int
Servlet的加载优先级,等价于标签
initParams
String
Servlet的初始化参数,指定一组 Servlet初始化参数,等价于标签
实例3-3 通过注解方式部署Servlet
本实例利用@WebServlet注解在Tomcat中部署Servlet。
源程序:AnnotationServlet.java的部分代码
@WebServlet( name="AnnotationServlet",
value="/AnnotationServlet",
initParams = {@WebInitParam(name="count", value = "100")},
loadOnStartup =5)
public class AnnotationServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println("");
out.println("节约粮食tips:向舌尖上的浪费说不,向光盘的您看齐。
");
out.println("");
}
}
操作步骤:
(1)右击com.example.ch03包,在弹出的快捷菜单中选择New→Servlet命令,然后输入名称AnnotationServlet,按Enter键确认建立文件。
(2)在AnnotationServlet.java文件中输入源代码。
(3)单击运行工具栏中的“运行”按钮,启动Tomcat。
(4)在浏览器中输入网址http://localhost:8080/ch03/AnnotationServlet,查看运行效果,如图3-8所示。
图3-8 实例3-3的运行效果
程序说明:
* 本实例中,若@WebServlet只有一个属性value="/AnnotationServlet",那么value和=符号也可以省略,即最简模式为@WebServlet("/AnnotationServlet")。
* 本书后续实例中的Servlet注解都采用最简模式。
?3.5
请求Servlet的三种方式
请求Servlet的常用方式有三种,分别为通过超链接方式请求、通过JSP页面或者 HTML页面的表单方式请求,以及通过JavaScript脚本的Ajax方式请求。
* 3.5.1 超链接请求Servlet
实例3-4 超链接请求Servlet
本实例利用超链接标签中的href属性请求Servlet。
源程序:young.html
超链接请求Servlet
学习古诗《教子诗》
源程序:YoungServlet.java的部分代码
@WebServlet("/YoungServlet")
public class YoungServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println("");
out.println("教子诗
");
out.println("【宋】余良弼
");
out.println("白发无凭吾老矣,青春不再汝知乎。
");
out.println("年将弱冠非童子,学不成名岂丈夫。
");
out.println("幸有明窗并净几,何劳凿壁与编蒲。
");
out.println("功成欲自殊头角,记取韩公训阿符。
");
out.println("");
}
}
操作步骤:
(1)右击com.example.ch03包,在弹出的快捷菜单中选择New→Servlet命令,然后输入名称YoungServlet,按Enter键确认建立文件。
(2)右击webapp文件夹,在弹出的快捷菜单中选择New→HTML命令,然后输入名称young,按Enter键确认建立文件。
(3)分别在young.html和YoungServlet.java文件中输入源代码。
(4)单击运行工具栏中的“运行”按钮,启动Tomcat。
(5)在浏览器中输入网址http://localhost:8080/ch03/young.html,查看浏览效果,如 图?3-9所示。
图3-9 young.html的运行效果
(6)单击页面上的超链接,请求访问YoungServlet,运行效果如图3-10所示。
图3-10 YoungServlet.java的运行效果
程序说明:
* 超链接属性href的值应与@WebServlet注解中value属性值一致,不包含“/”字符。
* 超链接被单击激活时,浏览器以Get方式发送请求。
* 在浏览器地址栏中输入请求Servlet的URL,与超链接请求效果一致。
* 3.5.2 表单请求Servlet
通过JSP页面或者HTML页面的表单请求Servlet,两者的方法是一致的。
实例3-5 表单请求Servlet
本实例利用HTML页面提交表单方式请求Servlet,实现登记无偿献血者信息的功能。
源程序:donate.html的部分代码
无偿献血登记
源程序:DonateServlet.java的部分代码
@WebServlet("/DonateServlet")
public class DonateServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
String bloodtype = request.getParameter("bloodtype");
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("");
out.println("无偿献血是无私奉献、救死扶伤的崇高行为。
");
out.println("感谢您!"+bloodtype+"型血的"+name+"朋友。
");
out.println("");
}
}
操作步骤:
(1)右击com.example.ch03包,在弹出的快捷菜单中选择New→Servlet命令,然后输入名称DonateServlet,按Enter键确认建立文件。
(2)右击webapp文件夹,在弹出的快捷菜单中选择New→HTML命令,然后输入名称donate,按Enter键确认建立文件。
(3)分别在donate.html和DonateServlet.java文件中输入源代码。
(4)单击运行工具栏中的“运行”按钮,启动Tomcat。
(5)在浏览器中输入网址http://localhost:8080/ch03/donate.html,查看运行效果,如图?3-11所示。
图3-11 donate.html的运行效果
(6)在页面上输入姓名,选择血型,单击“提交”按钮,运行效果如图3-12所示。
图3-12 DonateServlet.java的运行效果
程序说明:
* 表单属性action的值应与@WebServlet注解中value属性值一致,不包含“/”字符。
* 表单属性method的值应与Servlet中的服务方法对应,即get对应doGet方法,post对应doPost方法。
* “request.getParameter("name");”为读取表单中的姓名信息。
* “request.getParameter("bloodtype");”为读取表单中的血型信息,4.7.1节将详细介绍该方法。
* 通常情况下,Servlet的doGet与doPost方法中的处理请求代码相同,所以可以在doGet中编写处理请求的详细代码,而在doPost中调用doGet,从而优化代码量。
* 3.5.3 Ajax方法请求Servlet
实例3-6 Ajax方法请求Servlet
本实例利用JavaScript脚本中的Ajax方法请求Servlet,实现了在公益志愿者注册时校验用户名是否重复的功能。
源程序:register.html
公益志愿者用户注册