加载头像

第六章 Java Servlet 基础

6.1 servlet 的部署、创建与运行

Java Servlet 的核心思想是在 Tomcat 服务器端创建响应用户请求的 servlet 对象,简称 servlet。

源文件及字节码文件

Servlet 类

写一个创建 servlet 的类就是编写一个特殊类的子类,这个特殊的类就是 javax.servlet.http 或者 jakarta.servlet.http 包中的 HttpServlet 类。

HttpServlet 实现了 Servlet 接口,实现了响应用户的方法。

下面的代码是一个简单的 Servlet 类,为了便于 Web 应用程序的管理,Servlet 类应该具有包名,该类创建的 servlet 可以响应用户的请求,即用户请求这个 servlet 时,会在浏览器看到 Hello servlet 这样的响应信息:

HelloServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package ch6.moon.sun;

import java.io.*;
import jakarta.servlet.*;
import jakarta.servlet.http.*;

public class HelloServlet extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super.init(config);
}

public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设置响应的 MIME 类型
response.setContentType("text/html;charset=utf-8");

// 获得向用户发送数据的输出流
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + "Hello servlet" + "</h1>");
out.println("</body></html>");
}

public destroy() {

}
}

如果使用的 Tomcat 版本大于 10,javax.servlet-api 已经变成 jakarta.servlet-api,如果继续使用 javax 相关包,那么在 pom.xml 里面添加的依赖不匹配,会导致服务器无法实例化 servlet 类。

1
2
// Tomcat 10:当编写 servlet 类时,需要导入 jakarta 而不是 javax
import jakarta.servlet.http.*;

本站使用的 Tomcat 版本为 10.1.11,如果你使用的版本小于 10,请自行将本站代码中的 jakarta 修改为 javax。

字节码文件的保存
为了能让 Tomcat 服务器使用 HelloServlet 类创建一个 servlet,需要将该 Java 源文件产生的字节码文件按照类的包名对应的目录路径保存到 Web 服务目录中特定子目录中。包名 ch6.moon.sun 对应的目录路径是 ch6\moon\sun,因此,需要把 HelloServlet.class 保存到 Web 服务目录\WEB-INF\classes\ch6\moon\sun 中。

编写部署文件 web.xml

Servlet 类的字节码文件保存到指定的目录后,必须为 Tomcat 服务器编写一个部署文件,只有这样,Tomcat 服务器才会用 Servlet 类创建 servlet 对象。

该部署文件是一个 XML 文件,名字必须是 web.xmlweb.xml 由 Tomcat 服务器负责管理,使用该 XML 文件的系统应用程序(例如 Tomcat 服务器)配有内置的解析器,可以解析 XML 文件的标记中的数据。可以在 Tomcat 服务器的 webapps 目录中的 root 目录找到一个 web.xml 文件,参照它编写自己的 web.xml 文件。
编写的 web.xml 文件必须保存到 Web 服务目录的 WEB-INF 子目录中。根据 HelloServlet 类,web.xml 文件的内容如下:

web.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>ch6.moon.sun.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello_servlet</url-pattern>
</servlet-mapping>
</web-app>

6.2 servlet 的工作原理

servlet 由 Tomcat 服务器负责管理,Tomcat 服务器通过读取 web.xml,然后创建并运行 servlet。

servlet 对象的生命周期

当多个用户请求同一个 servlet 时,服务器为每个用户分别启动一个线程而不是共用一个进程,这些线程由 Tomcat 服务器来管理,与传统的 CGI 为每个用户清单一个进程相比较,效率要高得多。

一个 servlet 的生命周期主要由下列三个过程组成:

  1. 初始化 servlet。servlet 第一次被请求加载时,服务器初始化这个 servlet,即创建一个 servlet,这 servlet 调用 init 方法完成必要的初始化工作。
  2. 新诞生的 servlet 再调用 service 方法响应用户的请求。
  3. 当服务器关闭时,调用 destroy 方法销毁 servlet。

init 方法只被调用一次,即在 servlet 第一次被请求加载时调用该方法。当后续的用户请求 servlet 服务时,Tomcat 服务器将启动一个新的线程,在该线程中, servlet 调用 service 方法响应用户的请求。也就是说,每个用户的每次请求都导致 service 方法被调用执行,其执行过程分别运行在不同的线程中。

init 方法

该方法是 HttpServlet 类中的方法,可以在子类中重写这个方法。

init 方法的声明格式是:

1
2
3
public void init(ServletConfig config) throws ServletException {
// init 方法体
}

servlet 第一次被请求加载时,服务器创建一个 servlet,这个对象调用 init 方法完成必要的初始化工作。

该方法在执行时,服务器会把一个 SevletConfig 类型的对象传递给 init 方法,这个对象就被保存在 servlet 中,直到 servlet 被销毁。这个 ServletConfig 对象负责向 servlet 传递服务设置信息,如果传递失败就会发生 ServletException,servlet 就不能正常工作。

service 方法

该方法是 HttpServlet 类中的方法,可以在子类中直接继承该方法或重写这个方法。

service 方法的声明格式是:

1
2
3
public void service(HttpServletRequest request, HttpServletResponse response) throw ServletException, I0Exception {
// service 方法体
}

当 servlet 成功创建和初始化之后,调用 service 方法来处理用户的请求并返回响应。

Tomcat 服务器将两个参数传递给该方法。一个是 HttpServletRequest 类型的对象,该对象封装了用户的请求信息,另外一个参数对象是 HttpServletResponse 类型的对象,该对象用来响应用户的请求。和 init 方法不同的是,init 方法只被调用一次,而 service 方法可能被多次的调用。也就是说,当后续的用户请求该 servlet 时,Tomcat 服务器将启动一个新的线程,在该线程中 servlet 调用 service 方法响应用户的请求,即每个用户的请求都导致 service 方法被调用执行,调用过程运行在不同的线程中,互不干扰。因此,不同线程的 service 方法中的局部变量互不干扰,一个线程改变了自己的 service 方法中局部变量的值不会影响其他线程的 service 方法中的局部变量。

destroy 方法

该方法是 HttpServlet 类中的方法,子类可直接继承这个方法,一般不需要重写。

destroy 方法的声明格式是:

1
2
3
public destroy() {
// destroy 方法体
}

当 Tomcat 服务器终止服务时,例如关闭 Tomcat 服务器等,destroy 方法会被执行,销毁 servlet。

6.3 通过 JSP 页面访问 servlet

按照部署文件 web.xml 给出的 servlet 的 url-pattern,用户除了可以在浏览器输入 url-pattern 请求运行一个 servlet 外,也可以通过 JSP 页面来请求一个 servlet。

通过表单向 servlet 提交数据

Web 服务目录下的 JSP 页面都可以通过 form 表单请求该 Web 服务目录下的某个 servlet。如果 web.xml 文件中给出的 servlet 的 url-pattern 是 /computeBill,那么 form 表单中 action 给出的值就是 computeBill,如下所示:

1
2
<form action="computeBill">
</form>

当请求一个 servlet 时,也可以在请求的 url-pattern 中额外加入参数及其值,格式是:

1
url-pattern?参数1=值&参数2=值...参数n=值

例如:

1
2
<form action="computeBill?sideA=10.66&sideB=23.9&sideC=897">
</form>

通过 JSP 页面访问 servlet 的好处是,JSP 页面可以负责页面的信息显示,信息的有关处理交给 servlet 去完成。

通过超链接访问 servlet

JSP 页面可以使用超链接去请求某个 servlet。如果 web.xml 文件中给出的请求 servlet 的 url-pattern 是 /circle,那么超链接标记中 href 的值是 circle(不要写成 /circle):

1
<a href="circle"></a>

6.4 共享变量

Servlet 类是 HttpServlet 的一个子类,在编写子类时就可以声明某些成员变量,那么,请求 servlet 的用户将共享该 servlet 的成员变量。

6.5 doGet 和 doPost 方法

HttpServlet 类除了 init、service、destroy 方法外,该类还有两个很重要的方法:doGet 和 doPost,用来处理用户的请求并作出响应。

当 Tomcat 服务器创建 servlet 后,该 servlet 会调用 init 方法初始化自己,以后每当 Tomcat 服务器再接受一个对该 servlet 请求时,就会产生一个新线程,并在这个线程中让该 servlet 调用 service 方法。实际上 HttpServlet 类所给出的 service 方法的功能是检查 HTTP 请求类型(get、post 等),并在 service 方法中根据用户的请求方式,在 service 方法中对应地再调用 doGet 或 doPost 方法。因此,在编写的 Servlet 类(HttpServlet 类的一个子类)时,也可以不重写 service 方法来响应用户,直接继承 service 方法即可。

如果不重写 service 方法,就需要在 Servlet 类中重写 doPost 或 doGet 方法来响应用户的请求。如果不论用户请求类型是 post 还是 get,Tomcat 服务器的处理过程完全相同,那么可以只在 doPost 方法中编写处理过程,而在 doGet 方法中再调用 doPost 方法即可,或只在 doGet 方法中编写处理过程,而在 doPost 方法中再调用 doGet 方法。如果根据请求的类型进行不同的处理,就要在两个方法中编写不同的处理过程(这一点比 service 方法更为灵活)。

6.6 重定向与转发

重定向的功能是将用户从当前页面或 servlet 定向到另一个 JSP 页面或 servlet。

转发的功能是将用户对当前 JSP 页面或 servlet 的请求转发给另一个 JSP 页面或 servlet。

sendRedirect 方法

重定向方法 void sendRedirect(String location) 是 HttpServletResponse 类中的方法。当用户请求一个 servlet 时,该 servlet 在处理数据后,可以使用重定向方法将用户重新定向到另一个 JSP 页面或 servlet。重定向方法仅仅是将用户从当前页面或 servlet 定向到另一个 JSP 页面或 servlet,但不能将用户对当前页面或 servlet 的请求(HttpServletRequest 对象)转发给所定向的资源。

即重定向的目标页面或 servlet 无法使用 request 获取用户提交的数据。

forward 方法

RequestDispatcher 对象可以把用户对当前 JSP 页面或 servlet 的请求转发给另一个 JSP 页面或 servlet,而且将用户对当前 JSP 页面或 servlet 的请求传递给转发到的 JSP 页面或 servlet。

即当前页面所转发到的 JSP 页面或 servlet 可以使用 request 获取用户提交的数据。

下面介绍实现转发的步骤:

  1. 得到 RequestDispatcher 对象。用户所请求的当前 JSP 或 servlet 可以让 HttpServletRequest 对象 request 调用

    1
    public RequestDispatcher getRequestDispatcher(String path)

    方法返回一个 RequestDispatcher 对象,其中参数 path 是准备要转发到的 JSP 页面的 URL 或 servlet 的 url-pattern。例如:

    1
    2
    RequestDispatcher dispatcher = request.getRequestDispatcher("target.jsp");
    RequestDispatcher dispatcher = request.getRequestDispatcher("targetServlet");
  2. 转发。在步骤 1 中获取的 RequestDispatcher 对象调用

    1
    public void forward(ServletRequest request, ServletResponse response) throws ServletException, ava.io.IOException

    方法可以将用户对当前 JSP 页面或 servlet 的请求转发给 RequestDispatcher 对象所指定的 JSP 页面或 servlet,例如:

    1
    dispatcher.forward(request, response);

    把用户对当前 JSP 页面或 servlet 的请求转变为对转发到的 JSP 页面或 servlet 的请求。

二者的区别

转发(forward)和重定向方法(sendRedirect)不同的是,用户可以看到转发到的 JSP 页面或 servlet 的运行效果,但是,在浏览器的地址栏中不能看到 forward 方法转发到的 JSP 页面的地址或 servlet 的地址,用户在浏览器的地址栏中所看到的仍然是当前 JSP 页面的 URL 或 servlet 的 url-pattern。如果此时刷新浏览器,那么请求将是当前的 JSP 页面或 servlet。

另外,当 servlet 中执行 forward 方法实施转发操作时,Tomcat 会立刻结束当前 servlet 的执行。而 servlet 中执行 sendRedirect 方法重定向时,Tomcat 服务器还是要把当前的 servlet 代码执行完毕后才实施重定向(跳转)操作,但 Tomcat 服务器不再给用户看当前 servlet 代码的执行效果。如果在执行 sendRedirect 方法后,servlet 紧接着执行了 return 返回语句,那么 Tomcat 服务器会立刻结束当前 servlet 的执行。

使用转发的好处

使用转发技术可以让 JSP 页面和处理数据的 servlet 解耦,JSP 页面只需和处理转发的 servlet 打交道。

例如,在实际问题中,对数据的处理可能有多种需求,那么可以把这些需求分别指派给几个 servlet 来完成,然后编写一个 servlet 负责接收用户的请求,并根据用户的请求信息将用户的请求转发给这些 servlet 中的某个 servlet。当更新某个需求时,只需重新编译相应的 Servlet 类,这有利于 Web 应用程序的维护。

6.7 使用 session

HTTP 通信协议是用户与服务器之间一种请求与响应(request/response)的通信协议,属于无状态协议。所谓无状态是指,当用户(浏览器)发送请求给服务器,Tomcat 服务器作出响应后,如果同一个用户再发送请求给 Tomcat 服务器时,Tomcat 服务器并不知道就是刚才的那个用户。简单地说,Tomcat 服务器不会记录用户的信息。

用户在访问一个 Web 服务目录期间,Tomcat 服务器为该用户分配一个 session 对象(称为用户的会话),Tomcat 服务器可以在各个页面以及 servlet 中使用这个 session 记录用户的有关信息,而且 Tomcat 服务器保证不同用户的 session 对象互不相同。

HttpServletRequest 对象 request 调用 getSession 方法获取用户的 session 对象:

1
HttpSession session = request.getSession(true);

访问某个 Web 服务目录的用户,在不同的 servlet 中获取的 session 对象是完全相同的,不同的用户的 session 对象互不相同。

习题 6

  1. 假设 Web 服务目录 mymoon 中的 JSP 页面要使用一个 servlet,该 servlet 的包名为 blue.sky。请说明,应当怎样保存 servlet 的字节码文件。

    答:将创建 servlet 的字节码文件保存在 \mymoon\WEB-INF\classes\blue\sky 中。

  2. 假设 Web 服务目录是 mymoon,star 是 mymoon 的一个子目录,JSP 页面 a.jsp 保存在 star 中,a.jsp 准备请求一个 servlet,该 servlet 的包名为 tom.jiafei。下列哪个叙述是正确的?

    A. 创建 servlet 的字节码文件保存在 \mymoon\WEB-INF\classes\tom\jiafei

    B. 创建 servlet 的字节码文件保存在 \mymoon\star\WEB-INF\classes\tom\jiafei

    C. 创建 servlet 的字节码文件保存在 \mymoon\WEB-INF\star\classes\tom\jiafei

    D. 创建 servlet 的字节码文件保存在 \mymoon\WEB-INF\classes\start\tom\jiafei

  3. 假设 Web 服务目录是 mymoon,star 是 mymoon 的一个子目录,JSP 页面 a.jsp 保存在 star 中,a.jsp 准备请求一个 servlet,该 servlet 的包名为 tom.jiafei。下列哪个叙述是正确的?

    A. web.xml 文件保存在 \mymoon\WEB-INF\classes

    B. web.xml 文件保存在 \mymoon\WEB-INF\

    C. web.xml 文件保存在 \mymoon\WEB-INF\star\

    D. web.xml 文件保存在 \mymoon\star\WEB-INF\

  4. servlet 对象是驻留在服务器端,还是在客户端被创建?

    答:servlet 对象驻留在服务器端,只会把结果传给客户端显示。

  5. servlet 对象被创建后将首先调用 init 方法还是 service 方法?

    答:servlet 对象被创建后将首先调用 init 方法进行初始化。

  6. “servlet 第一次被请求加载时调用 init 方法。当后续的客户请求 servlet 对象时,servlet 对象不再调用 init 方法”,这样的说法是否正确?

    答:正确,servlet 第一次被请求加载时,服务器先进行类加载、创建对象、调用 init 方法初始化、service 方法等,后续客户再访问时,之前的工作都不需要执行了,直接执行 service 方法。

  7. servlet 第一次被请求加载后,当后续的客户请求 servlet 对象时,下列哪个叙述是正确的?

    A. servlet 调用 service 方法

    B. servlet 调用 init 方法

    C. servlet 调用 doPost 方法

    D. servlet 调用 doGet 方法

  8. 假设创建 servlet 的类是 tom.jiafei.Dalian,创建的 servlet 对象的名字是 myservlet,应当怎样配置 web.xml 文件?

    答:将 web.xml 文件保存在服务目录下的 WEB-INF 目录中:

    web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app>
    <servlet>
    <servlet-name>myservlet</servlet-name>
    <servlet-class>tom.jiafei.Dalian</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>myservlet</servlet-name>
    <url-pattern>/myservlet</url-pattern>
    </servlet-mapping>
    </web-app>
  9. 如果 Servlet 类不重写 service 方法,那么应当重写哪两个方法?

    答:应当重写 doGet 方法和 doPost 方法。

  10. HttpServletResponse 类的 sendRedirect 方法和 RequestDispatcher 类的 forward 方法有何不同?

    答:转发(forward)和重定向方法(sendRedirect)不同的是,用户可以看到转发到的 JSP 页面或 servlet 的运行效果,但是,在浏览器的地址栏中不能看到 forward 方法转发到的 JSP 页面的地址或 servlet 的地址,用户在浏览器的地址栏中所看到的仍然是当前 JSP 页面的 URL 或 servlet 的 url-pattern。如果此时刷新浏览器,那么请求将是当前的 JSP 页面或 servlet。

    另外,当 servlet 中执行 forward 方法实施转发操作时,Tomcat 会立刻结束当前 servlet 的执行。而 servlet 中执行 sendRedirect 方法重定向时,Tomcat 服务器还是要把当前的 servlet 代码执行完毕后才实施重定向(跳转)操作,但 Tomcat 服务器不再给用户看当前 servlet 代码的执行效果。如果在执行 sendRedirect 方法后,servlet 紧接着执行了 return 返回语句,那么 Tomcat 服务器会立刻结束当前 servlet 的执行。

  11. servlet 对象怎样获得用户的会话对象?

    答:通过在 servlet 对象中传入一个 request 对象,调用 getSession 方法来获取用户的会话对象。


返回目录 上一章 下一章
博客快捷键
shift K
关闭快捷键功能
shift A
打开/关闭中控台
shift M
播放/暂停音乐
shift D
深色/浅色显示模式
shift S
站内搜索
shift R
随机访问
shift H
返回首页
shift L
友链页面
shift P
关于本站
shift I
原版/本站右键菜单
引用到评论
随便逛逛博客分类文章标签
复制地址关闭热评深色模式轉為繁體