■ 学习目标

  • 了解Servlet用途
  • 理解Servlet的生命周期
  • 掌握Servlet的运行环境以及Servlet的体系结构
  • 掌握Servlet的配置与执行
  • 理解如何使用Web程序和Servlet进行交互
  • 了解相关接口

目录:


Servlet介绍

Servlet技术是Sun公司提供的一种实现动态网页的解决方案,它是基于Java编程语言的Web服务器端编程技术,主要用于在Web服务器端获得客户端的访问请求信息和动态生成对客户端的响应消息。同时,Servlet技术也是JSP技术的基础。

什么是Servlet

Servlet是服务器端的Java小程序,可以被用来通过多种方法扩充一个Web服务器的功能。Servlet可以对客户端的请求进行响应,在默认情况下,Servlet采用一种无状态的请求-响应处理方式。

Servlet主要作用是为了增强Java服务器端功能。

一个Servlet程序就是一个实现了特殊接口的Java类,它由支持Servlet的Web服务器(具有Servlet引擎)调用和启动运行。

一个Servlet程序负责处理它所对应的一个或一组URL地址的访问请求,并用于接收客户端发出的访问请求信息和产生响应的内容。

Servlet程序可以完成的任务:

  • 获取客户端通过HTML的FORM表单递交的数据和URL后面的参数信息
  • 创建对客户端的响应消息内容
  • 访问服务器端的文件系统
  • 连接数据库并开发基于数据库的应用
  • 调用其它的Java类

10-1-1.png

使用Servlet的基本流程:

  1. 客户端通过HTTP提出请求。
  2. Web服务器接收该请求交给Servlet容器,然后再调用Servlet中的方法来处理。如果这个Servlet尚未被加载,Servlet容器将把它加载到Java虚拟机并且执行它。
  3. Servlet将接收该HTTP请求并用特定的方法进行处理:可能会访问数据库、调用Web服务、EJB调用或直接给出结果, 并生成一个响应。
  4. 这个响应由Servlet容器返回给Web服务器。
  5. Web服务器包装这个响应,以HTTP响应的方式发送给Web浏览器。

Servlet技术特点

  • Servlet是用Java编写的,所以它与平台无关。
  • Servlet是持久的。
  • Servlet是可扩展的。
  • Servlet是安全的。

JSP与Servlet 的关系

Servlet是服务器端运行的一种Java应用程序。当浏览器端有请求则将其结果传递给浏览器。

在JSP中使用到的所有对象都将被转换为Servlet或者非Servlet的Java对象,然后被执行,所以执行JSP实际上与执行Servlet是一样的。

10-1-3.png

Servlet与JSP相比有以下几点区别:

  • 编程方式不同;
  • Servlet必须在编译以后才能执行;
  • 运行速度不同:由于JSP容器将JSP程序编译成Servlet的时候需要一些时间,所以JSP的运行速度比Servlet要慢一些,不过,如果JSP文件能毫无变化的重复使用,它在第一次以后的调用中运行速度就会和Servlet一样了,这是因为JSP 容器接到请求以后会确认传递过来的JSP是否有改动,如果没有改动的话,将直接调用JSP编译过的Servlet类,并提供给客户端解释执行,如果JSP文件有所改变,JSP 容器将重新将它编译成Servlet,然后再提交给客户端;
  • Servlet用来写业务逻辑层是很强大的,但是对于写表示层就很不方便。JSP则主要是为了方便写表示层而设计的。

Servlet的工作原理

Servlet是javax.Servlet包中HttpServlet类的子类,运行在Web服务器的Servlet容器里

Servlet容器:根据Servlet的生命周期的规范,负责执行Servlet对象的初始化、运行和卸载等动作

Servlet的生命周期:Servlet在容器中从创建到删除的过程

10-1-4.png

Servlet的生命周期可分为下面几个阶段:

  1. 装载Servlet
    • Servlet容器启动时自动加载某些Servlet;
    • 在Servlet容器启动后,客户首次向Servlet发出请求;
    • Servlet的类文件被更新后,重新加载Servlet。
  2. 实例化一个Servlet实例对象。
  3. 调用Servlet的init( )方法进行初始化。
  4. 服务方法:容器收到对该Servlet的请求,则调用该Servlet对象的service()方法处理请求。
  5. 卸载:当服务器端不再需要该Servlet的时候,服务器调用destroy()方法卸载该Servlet,释放Servlet运行时占用的资源。

Servlet常用接口和类

10-1-5.png

Servlet接口、GenericServlet类、HttpServlet类

HttpServlet -> extends GenericServlet -> implements Servlet

Servlet是最顶层的接口,其提供的方法有 :

init(ServletConfig config) : void   // 初始化   
getServletConfig() : ServletConfig  // 取得该Servlet配置信息
getServletInfo() : String           // 取得相关信息
service(ServletRequest req, ServletResponse res) : void  //核心方法
destroy() : void   // Servlet生命周期结束时候执行的方法

显然我们最关心的是service方法,其他的几个方法在实现的时候是千篇一律、无关痛痒的。故提供了GenericServlet类,此类实现了Servlet接口,我们在使用Servlet的时候,只需继承这个类然后覆盖其中的service方法(抛出ServletExceptionIOException异常)即可。

由于Servlet基本上是在http协议下使用的,故提供了HttpServlet这个类,此类继承自GenericServlet类,我们在使用Servlet时,只需继承HttpServlet类然后覆盖以下方法 :

service(HttpServletRequest request , HttpServletResponse response) throws ServletException, IOException

注意:HttpServletRequestHttpServletResponse分别是从ServletRequestServletResponse继承

此外,HttpServlet还提供了doPostdoGet方法,参数和返回值与service方法一样。只是service方法可以针对客户端的任何请求类型(GET和POST),而doPostdoGet方法分别只能对应客户端的POST方式请求和GET方式的请求。

Servlet的三种实现方式

  • 第一种创建方式:继承HttpServlet(==最优==)
    • 重写doGet(HttpServletRequest request, HttpServletResponse response)方法
    • 重写 doPost(HttpServletRequest request, HttpServletResponse response)
  • 第二种创建方式:实现接口Servlet
    • 重写getServletConfig()方法
    • 重写getServletInfo()方法
    • 重写init(ServletConfig arg0)方法
    • 重写service(ServletRequest request, ServletResponse response)方法
    • 重写destroy()
  • 第三种创建方式:继承GenericServlet类
    • 重写service(ServletRequest request, ServletResponse response)方法

开发部署一个简单的Servlet

Servlet文件创建

创建一个Servlet,通常涉及下列4个步骤:

  1. 继承HttpServlet抽象类。
  2. 重载适当的方法,如覆盖(或称为重写)doGet()方法或doPost()方法。
  3. 如果有HTTP请求信息的话,获取该信息。
  4. 生成HTTP响应。
    HttpServletResponse类对象生成响应,并将它返回到发出请求的客户机上。它的方法允许设置“请求”标题和“响应”主体。“响应”对象还含有getWriter()方法以返回一个PrintWriter类对象。使用PrintWriterprint()方法和println()方法以编写Servlet响应来返回给客户机,或者直接使用out对象输出有关HTML文档内容。

10-2-2.png

  • Servlet程序必须通过Servlet引擎来启动运行,并且储存目录有特殊要求,通常需要存储在<WEB应用程序目录>\WEB-INF\classes\目录中。

  • Servlet程序必须通过@WebServlet注解配置Servlet或者在WEB应用程序的web.xml文件中 进行注册和映射其访问路径,才可以被Servlet引擎加载和被外界访问。

  • Servlet的生命周期定义了一个Servlet如何被加载、初始化,以及它怎样接收请求、响应请求、提供服务。Servlet的生命周期如图。

10-2-1.png

@WebServlet

@WebServlet注解配置Servlet

从Servlet3.0开始,配置Servlet支持注解方式,但还是保留了配置web.xml方式,所以使用Servlet有两种方式:

  1. Servlet类上使用@WebServlet注解进行配置

    Servlet3.0 规范可通过Annotation来配置管理Web组件,因此web.xml文件可以变得更加简洁,这也是Servlet3.0 的重要简化。

  2. web.xml文件中配置

    Servlet所有版本都支持

@WebServlet常用属性

属性名 类型 描述
asyncSupported Boolean 声明Servlet是否支持异步操作模式,等价于<async-supported> 标签
description String Servlet 的描述信息,等价于<description>标签
displayName String Servlet的显示名称,通常配合工具使用,等价于<display-name>标签
initParams String Servlet的初始化参数,指定一组 Servlet 初始化参数,等价于<init-param>
name String Servlet的名称,等价于<servlet-name>。没有显式指定,则该 Servlet 的取值即为类的全限定
urlPatterns String[ ] Servlet的访问URL,指定一组 Servlet 的 URL 匹配模式。等价于<url-pattern>标签
value String[ ] Servlet的访问URL,属性等价于 urlPatterns 属性。两个属性不能同时指定
loadOnStartup int Servlet的加载顺序,等价于<load-on-startup>标签

如果打算使用Annotation来配置Servlet,需要注意以下几点:

  • 不要在web.xml文件的根元素<web-app---/>中指定metadata-complete=“true”
  • 不要在web.xml文件中配置该Servlet;
  • Servlet 3.0 的部署描述文件 web.xml 的顶层标签<web-app> 有一个 metadata-complete 属性,该属性指定当前的部署描述文件是否是完全的。如果设置为 true,则容器在部署时将只依赖部署描述文件,忽略所有的注解;如果不配置该属性,或者将其设置为 false,则表示启用注解支持(和可插性支持)。

这些属性均为可选属性,但是 vlaue 或者 urlPatterns 通常是必需的,且二者不能共存,如果同时指定,通常是忽略 value 的取值。

  1. loadOnStartup属性:标记容器是否在启动应用时就加载Servlet,默认不配置或数值为负数时表示客户端第一次请求Servlet时再加载;0或正数表示启动应用就加载,正数情况下,数值越小,加载该Servlet的优先级越高;
  2. name属性:可以指定也可以不指定,通过getServletName()可以获取到,若不指定,则为Servlet的完整类名,如:cn.edu.njit.servlet.UserServlet
  3. urlPatterns/value属性: String[]类型,可以配置多个映射,如:urlPatterns={“/user/test”, “/user/example”}
  4. 在使用注解方式时,需要注意:<web-app> </web-app>根元素中不能配置属性metadata-complete="true" ,否则无法加载Servlet。metadata-complete属性表示通知Web容器是否寻找注解,默认不写或者设置false,容器会扫描注解和Web分片,为Web应用程序构建有效的元数据;设置true,表示将由部署描述符为Web程序提供所有的配置信息web.xml中不能再配置该Servlet

Servlet的配置文件

在web.xml文件中对Servlet进行配置

Servlet的名称、类和其他选项的配置
配置Servlet时,必须指定Servlet的名称、Servlet的类的路径,可选择性地给Servlet添加描述信息和指定在发布时显示的名称。

<servlet>
<description></description>
<display-name>Test</display-name>
<servlet-name>Test</servlet-name>
<servlet-class>com.TestServlet</servlet-class>
</servlet>
  • Description元素描述的是Servlet的描述信息
  • display-name元素描述的是发布时Servlet的名称
  • Servlet-name元素描述的是Servlet的名称
  • Servlet-class是Servlet类的路径。

初始化参数

Servlet可以配置一些初始化参数

<Servlet>
    <init-param>
        <param-name>number</param-name>
        <param-value>100</param-value>
    </init-param>
</Servlet>

指定参数number的参数值为100。在Servlet中可以在init()方法体中通过getInitParameter()方法访问这些初始化参数。

启动装入优先权

启动装入优先权通过<load-on-startup>元素指定

<Servlet>
    <Servlet-name>ServletOne</Servlet-name>
    <Servlet-class>com.ServletOne</Servlet-class>
    <load-on-startup>5</load-on-startup>
</Servlet>
<Servlet>
    <Servlet-name>ServletTwo</Servlet-name>
    <Servlet-class>com.ServletTwo</Servlet-class>
    <load-on-startup>10</load-on-startup>
</Servlet>
<Servlet>
    <Servlet-name>ServletThree</Servlet-name>
    <Servlet-class>com.ServletThree</Servlet-class>
    <load-on-startup>AnyTime</load-on-startup>
</Servlet>

ServletOne类先被载入,ServletTwo类则后被载入,而ServletThree类可在任何时间内被载入

Servlet的映射

在web.xml配置文件中可以给一个Servlet做多个映射
可以通过不同的方法访问这个Servlet

<servlet-mapping>
    <servlet-name>Test</servlet-name>
    <url-pattern>/Test</url-pattern>
</servlet-mapping>

若请求的路径中包含/Test,则会访问逻辑名为Test的Servlet。

<Servlet-mapping>
    <Servlet-name>OneServlet</Servlet-name>
    <url-pattern>/Two/*</url-pattern>
</Servlet-mapping>

若请求的路径中包含/Two/a/Two/b等符合/Two/*的模式,则同样会访问逻辑名为OneServlet的Servlet。

注意:

  • 在web.xml文件中所有元素出现的次序是有严格限制的
  • <Servlet>元素必须出现在<Servlet-mapping>元素之前。

例子

package ch10;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.*;

// HelloWorld Servlet
public class HelloWorld extends GenericServlet {
    public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException {  
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}
 <!-- HelloServlet definition -->
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>ch16.HelloWorld</servlet-class>
  </servlet>

  <!-- HelloServlet mapping -->
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/sayHello</url-pattern>
  </servlet-mapping>

Servlet实现相关的接口和类

Servlet

声明: public interface Servlet
这个接口是所有Servlet必须直接或间接实现的接口

1、init()方法 :

  • init(ServletConfig config) throws ServletException
  • 由 Servlet 容器调用,指示将该 Servlet 放入服务。 Servlet 容器仅在实例化 Servlet 之后调用 init 方法一次。在 Servlet 可以接收任何请求之前,init 方法必须成功完成
  • 如果 init() 方法抛出 ServletException 或者未在 Web 服务器定义的时间段内返回,那么Servlet 容器无法将 Servlet 放入服务。方法包含 Servlet 的配置和初始化参数的 ServletConfig 对象 。如果发生妨碍 Servlet 正常操作的异常则Throws ServletException

2、service()方法

  • 当一个Servlet对象被初始化后,该对象就活动在容器内,在容器的协助下,接收请求和发送响应。
  • void service(ServletRequest request,ServletRespone response)
    该方法有ServletRequestServletResponse两个类型的参数,它们分别是“接收请求”和“响应回复”的句柄。

3、destroy()方法

  • 服务器可以从内存中移除已经加载的Servlet,也可以在所有的线程都完成以后或超过了设定的期限时卸载Servlet。
  • 在将Servlet卸载之前, 调用destroy()方法。
  • 方法的描述为:public destroy()
    destroy()方法可以通过Servlet容器的垃圾处理器回收资源,并释放其所占的资源和内存,特别是不能被Java垃圾回收机制回收的资源。

4、getServletConfig()方法

  • 此方法返回 ServletConfig 对象,该对象包含此 Servlet 的初始化和启动参数。返回的 ServletConfig 对象是传递给 init 方法的对象。
  • ServletConfig接口的实现负责存储 ServletConfig 对象,以便此方法可以返回该对象。

5、getServletInfo()方法

  • 此方法返回包含 Servlet 信息的 String,比如作者、版本和版权。
  • 此方法返回的字符串应该是纯文本

GenericServlet

声明:

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable
  • GenericServlet提供了对Servlet接口的基本实现
  • 当创建普通的和HTTP无关的操作时可以通过继承该类创建新的Serlvet
  • GenericServlet 是个抽象类,它的 service()方法是一个抽象方法,GenericServlet 的派生类必须直接或者间接地实现这个方法

HttpServlet

声明:

public Abstract class HttpServlet extends GenericServlet implements java.io.Serializable 
  • HttpServlet类是抽象类,继承自GenericServlet
  • 用于快速开发应用于HTTP协议的Servlet类。提供了Servlet接口中具体于HTTP的实现。
  • 一般用户自定义的Servlet都要扩展该类。

HttpServlet类中提供了两个service()的重载方法:

    public void service(ServletRequest req, ServletResponse res)throws ServletException, java.io.IOException

将接收的req和res转换为HttpServletRequestHttpServletResponse类型,并分发给受保护的service方法。

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException

接收来自public service方法的标准 HTTP 请求,并将它们分发给此类中定义的doXXX方法。

HttpServlet 的子类至少必须重写一个方法,该方法通常是以下这些方法之一:

  • doGet():处理http的get请求,从http服务器上取得资源。
  • doPost():处理http的post请求,主要用于发送HTML文本中FORM的内容。
  • doHead():用于处理HEADER请求。
  • doOptions():该操作自动决定支持什么HTTP方法
  • doPut():处理http的put请求,模仿 ftp发送资源。
  • doTrace():处理HTTP的trace请求。
  • doDelete():处理http的delete请求,从服务器上移出一个url。

这7个方法都是受保护类型的方法。

当容器为收到一个HttpServlet类型的请求时,该对象中的方法被调用顺序为:

  1. 调用public的service()方法。
  2. 在把参数分别转换为HttpServletRequestHttpServletResponse后,这个public的service()方法调用protected的service()方法。
  3. 根据HTTP请求方法的类型,protected的service()调用doXXX()方法之一。

Servlet请求和响应相关

HttpServletRequest接口

声明:

public interface javax.Servlet.http.HttpServletRequest implements ServletRequest
  • Servlet是请求驱动
    • Web容器收到一个对Servlet的请求时,就把这个请求封装成一个HttpServletRequest对象
    • 然后把对象传给Servlet的相应服务方法。
  • 获取客户端信息主要是通过调用ServletRequest接口或者子接口HttpRequest提供的方法。
  • 接口中的主要方法如表12-4所示。

HttpServletResponse接口

声明:

public interface javax.Servlet.http.HttpServletResponse implements ServletResponse
  • HttpServletResponse接口存放在javax.Servlet.http包内
  • 代表了对客户端的HTTP响应。
  • HttpServletResponse接口给出了响应客户端的Servlet方法。它允许Serlvet设置内容长度和回应的MIME类型,并且提供输出流ServletOutputStream
  • HttpServletResponse接口的主用方法如表12-5所示。

Servlet配置相关

  • 环境API 接口ServletConfig和ServletContext可以获得Servlet执行环境的相关数据。
    • ServletConfig对象接收Servlet特定的初始化参数,
    • ServletContext接收webapp初始化参数。
    • mServletConfig用作配置Servlet。
  • 这两个类都在javax.Servlet包中

ServletConfig接口

声明:public interface javax.Servlet.ServletConfig

  • ServletConfig接口用作配置Servlet
    • Servlet配置包括Servlet名字、Servlet的初始化参数和Servlet上下文。
    • Servlet引擎通过init(ServletConfig config)方法和GenericServlet.getServletConfig()方法获得ServletConfig对象。
  • 这个接口的主要方法如表12-6所示。
方 法 名 作 用
public String getServletName() 返回此 Servlet 实例的名称。该名称可能是通过服务器管理提供的, 在 Web 应用程序部署描述符中分配,或者对于未注册(和未命名)的 Servlet 实例,该名称将是该 Servlet 的类名称
public String getInitParameter(String name) 返回包含指定初始化参数的值的 String,如果参数不存在,则返回 null。该方法从 Servlet 的 ServletConfig 对象获取指定参数的值
public java.util.Enumeration getInitParameterNames() 以 String 对象的 Enumeration 的形式返回 Servlet 的初始化参数的名称, 如果 Servlet 没有初始化参数,则返回一个空的 Enumeration
public ServletContext getServletContext() 返回对调用者在其中执行操作的 ServletContext 的引用。调用者用一 个 ServletContext 对象与 Servlet 容器交互

获取 Servlet 配置信息的例子

【例10-4】获取 Servlet 自身信息、服务器端信息、客户端信息。

(1) 编写 Servlet 实例 ServletInfo.java,代码如下:

package com;
import java.io.*;
import java.util.*;
import javax.servlet.*;

// 获取自身信息的 Servlet
public class ServletInfo extends GenericServlet {
    private Map initParams = new LinkedHashMap();
    private String servletName = null;
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        // 获得初始化参数名称集合
        Enumeration paramNames = getInitParameterNames();
        // 获得所有参数的初始值
        while (paramNames.hasMoreElements()) {
            String name = (String) paramNames.nextElement();
            // 按参数名获得参数的初始值
            String value = getInitParameter(name);
            initParams.put(name, value);
        }
        // 获得 Servlet 的名称
        servletName = getServletName();
    }

    public void service(ServletRequest request, ServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=GB2312");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>获取 Servlet 自身的信息</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>Servlet 自身的信息: </h2>");
        out.println("<h4>配置名称: " + servletName + "</h4><br>");
        out.println("<h4>初始参数:</h4>");
        out.println("<table width=\"350\" border=\"1\">");
        out.println("<tr>");
        out.println("<td width=\"175\">参数名</td>");
        out.println("<td width=\"175\">参数值</td>");
        out.println("</tr>");
        Set paramNames = initParams.keySet();
        Iterator iter = paramNames.iterator();
        while (iter.hasNext()) {
            String name = (String) iter.next();
            String value = (String) initParams.get(name);
            out.println("<tr>");
            out.println("<td>" + name + "</td>");
            out.println("<td>" + value + "</td>");
            out.println("</tr>");
        }
        out.println("</table>");
        out.println("</body>");
        out.println("</html>");
    }
}

(2) 在 web.xml 文件中配置该 Servlet,代码如下:

<web-app>
    …
    <!-- GetServerInfo definition -->
    <servlet>
        <display-name>ServletInfo</display-name>
        <servlet-name>ServletInfo</servlet-name>
        <servlet-class>com.ServletInfo</servlet-class>
        <init-param>
            <param-name>Purpose</param-name>
            <param-value>getServletInfo</param-value>
        </init-param>
        <init-param>
            <param-name>Date</param-name>
            <param-value>2018-03-27</param-value>
        </init-param>
        <init-param>
            <param-name>Developping tool</param-name>
            <param-value>MyEclipse</param-value>
        </init-param>
    </servlet>
    <!-- GetServerInfo mapping -->
    <servlet-mapping>
        <servlet-name>ServletInfo</servlet-name>
        <url-pattern>/showServletInfo</url-pattern>
    </servlet-mapping>
    …
</web-app>

<servlet>元素内部使用<init-param>子元素来为 ServletInfo 配置初始化参数。在 ServletInfo.java
init()方法中通过 Enumeration paramNames = getInitParameterNames();获得所有参数名字,进
而根据名字获取每个参数的值。

(3) 在 Tomcat 服务器下测试,在浏览器地址栏输入http://localhost:8080/ch10/showServletInfo可以看到 ServletInfo 的信息

10-5-2-1.png

【例 10-5】获取服务器信息。在 Servlet 实例中可以获取服务器端信息,并在客户端显示。

(1) 编写 Servlet 实例 ServerInfoServlet.java,代码如下:

package com;
import java.io.*;
import java.util.*;
import javax.servlet.*;

public class ServerInfoServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    private Map initParams = new LinkedHashMap();
    private String servletName = null;

    public void service(ServletRequest request, ServletResponse response) throws
            ServletException, IOException {
        response.setContentType("text/html;charset=GB2312");
        PrintWriter out = response.getWriter();
        ServletContext sc = getServletContext();
        out.println("<html><body><head>");
        out.println("<title>获取服务器端信息</title>");
        out.println("</head><body>");
        out.println("<h2>服务器端信息: </h2>");
        out.println("<table width=\"500\" border=\"1\">");
        out.println("<tr>");
        out.println("<td width=\"175\">站点名</td>");
        out.println("<td width=\"325\">" + request.getServerName() + "</td>");
        out.println("</tr>");
        out.println("<tr>");
        out.println("<td>端口号</td>");
        out.println("<td>" + request.getServerPort() + "</td>");
        out.println("</tr>");
        out.println("<tr>");
        out.println("<td>服务器类型</td>");
        out.println("<td>" + sc.getServerInfo() + "</td>");
        out.println("</tr>");
        out.println("<tr>");
        out.println("<td>支持 Servlet 版本</td>");
        out.println("<td>" + sc.getMajorVersion() + "." + sc.getMinorVersion() + "</td>");
        out.println("</tr>");
        out.println("<tr>");
        out.println("<td>服务器属性</td>");
        out.println("<td>");
        // 获得服务器属性集合
        Enumeration attributes = sc.getAttributeNames();
        while (attributes.hasMoreElements()) {
            String name = (String) attributes.nextElement();
            out.println(name);
        }
        out.println("</td>");
        out.println("</tr>");
        out.println("</table>");
        out.println("</body>");
        out.println("</html>");
    }
}

(2) 在 web.xml 文件中配置该 Servlet,代码如下:

<web-app>
    …
    <!-- ServerInfoServlet definition -->
    <servlet>
        <description></description>
        <display-name>ServerInfoServlet</display-name>
        <servlet-name>ServerInfo</servlet-name>
        <servlet-class>com.ServerInfoServlet</servlet-class>
    </servlet>
    <!--ServerInfoServlet mapping -->
    <servlet-mapping>
        <servlet-name>ServerInfo</servlet-name>
        <url-pattern>/ServerInfo</url-pattern>
    </servlet-mapping>
    ...
</web-app>  

(3) 在 Tomcat 服务器下测试,在浏览器地址栏输入http://localhost:8080/ch10/ServerInfo可以看到 ServerInfoServlet 的信息

10-5-2-2.png

Servlet中的会话追踪

  • 会话是客户端发送请求,服务器返回响应的连接时间段。会话简单地将无状态的HTTP协议转换成高度集成的无缝活动线程,这使得Web应用程序感觉上就像一个应用程序。

  • Servlet引擎为每个连接分配了唯一的一个ID,并且在建立会话时将它分配给客户端 -> 客户端将该ID发送给所有后续请求的服务器 -> 通知会话结束。

  • 引擎可以将每个请求映射到特定的会话。

  • javax.Servlet.http.HttpSession接口是Servlet提供会话追踪解决方案。

获得一个HttpSession实例对象

使用HttpServletRequestgetSession()方法访问HttpSession对象。如果系统没有找到与请求关联的会话ID,true表示返回新会话。false表示方法返回null

语法为:HttpSession session = request.getSession();

访问和设置与会话相关联信息,维护会话的状态

使用HttpSessiongetAttribute()方法和setAttribute(String key,Object value)方法读取和设置当前请求会话数据(即对散列表的操作),维护会话的状态。

语法为:

public Object getAtrribute (string name); 
public void setAttribute(String name,Object value); 
`setAttribute`方法会替换任何之前的属性。如果不想被替换,则需要在设置之前使用`removeArrtibute(String key)`方法移除该属性。   

废弃会话数据

只移除自己编写的的Servlet创建的数据:removeArrtibute(String key)方法
(web应用程序中删除)删除整个会话:invalidate()方法,可以用该方法注销用户。
(Web服务器中删除)将用户从系统中注销并且删除所有与该会话关联的会话:logout()方法。

会话超时时间间隔

getMaxInactiveInterval()方法和setMaxInactiveInterval()方法读取和设置在没有访问的情况下,会话保存的最长时间。秒为单位。负数表示会话从不超时。超时由服务器来维护。

HttpSession 接口

HttpSessionjava.Servlet.http 包中的接口, 它封装了会话的概念。

其声明的语法格式如下:public interface javax.Servlet.http.HttpSession

HttpSession 接口常用的方法:

类 别 方 法 说 明
getAttribute() 获得一个属性的值 属性
getAttributeNames() 获得所有属性的名称
removeAttribute() 删除一个属性
setAttribute() 添加一个属性
getCreationTime() 获得会话首次的构建时间 会话值
getId() 获得每个会话所对应的唯一标志符
getLastAccessedTime() 获得最后一次访问时间,是毫秒数
getMaxInactiveInterval() 获得最大活动间隔
isNew() 判断 session 是否新
setMaxInactiveInterval() 设置最大的不活动间隔,单位是秒
生命周期 invalidate() 将会话作废,释放与之关联的对象

HttpSession 进行会话控制的过程中使用的方法如下。

  1. 获得一个 HttpSession 实例对象。使用 HttpServletRequestgetSession()方法访问 HttpSession 对象。如果系统没有找到与请求关联的会话 ID, true 表示返回新会话, false 表示方法返回 null。语法格式如下:
    HttpSession session = request.getSession();

在后台,系统从 Cookie 或 URL 重写附加的数据中提取出用户 ID。以 ID 为 key,遍历之前创建的 HttpSession 对象内建的散列表。如果找不到匹配的会话 ID,系统重新创建一个新的会话。默认情况下(不禁用 Cookie)还会创建一个名为 JSESSIONID,值为唯一标识用户表示会话 ID 的输出 Cookie。因为调用 getSession()方法会影响到后面的响应,所以只能在发送任何文档内容到客户端之前调用 getSession()方法。

  1. 访问和设置与会话相关联的信息,维护会话的状态。使用 HttpSessiongetAttribute()方法和 setAttribute(String key, Object value)方法读取并设置当前请求会话数据(即对散列表的操作),维护会话的状态。语法格式如下:
   public Object getAttribute (String name);
   public void setAttribute(String name,Object value);

setAttribute()方法会替换任何之前的属性。如果不想被替换,则需要在设置之前使用
removeAttribute(String key)方法移除该属性。

  1. 废弃会话数据。
  • 只移除自己编写的 Servlet 创建的数据: removeAttribute(String key)方法。
  • (在 Web 应用程序中)删除整个会话:可以用invalidate()方法注销用户。
  • (在 Web 服务器中)将用户从系统中注销并且删除所有与该会话关联的会话: logout()方法。
  • 会话超时时间间隔。 getMaxInactiveInterval()方法和setMaxInactiveInterval()方法读取并设置在没有访问的情况下,会话保存的最长时间,秒为单位。负数表示会话从不超时,超时由服务器来维护。

Servlet上下文

ServletContext接口

声明:public interface javax.Servlet .ServletContext

定义了一个Servlet的环境对象,通过这个对象,Servlet引擎向Servlet提供环境信息。

一个Servlet的环境对象与它所驻留的主机是一一对应的,在一个处理多个虚拟主机的Servlet引擎中,每一个虚拟主机都必须被视为一个单独的环境。

获得ServletContext对象:通过ServletConfig.getServletContext()方法和GenericServlet.getServletContext()方法。

ServletContext对象是服务器上一个Web应用的代表,它的多数方法都是用来获取服务器端信息。

Servlet协作

声明:public interface javax.Servlet. RequestDispatcher

  • 实现Servlet协作,它可以把一个请求转发到另一个Servlet。
  • 定义接收来自客户端的请求并将它们发送到服务器上的任何资源(比如 Servlet、HTML 文件或 JSP 文件)的对象。
  • Servlet 容器可创建 RequestDispatcher 对象,该对象被用作包装位于特定路径上的服务器资源或通过特定名称给定的服务器资源的包装器。

得到RequestDispatcher对象:

利用ServletContext接口中利用ServletContext接口中getNamedDispatcher(String path)方法

ServletConfig config = getServletConfig();
ServletContext context = config.getServletContext();
RequestDispatcher dispatcher = context.getRequestDispatcher(String path);

利用ServletContext接口中getNamedDispatcher(String path)方法

RequestDispatcher dispacher=
getServletConfig().getServletContext().getNamedDispatcher (String path);

利用ServletRequest接口中的getRequestDispatcher(String path)方法

RequestDispatcher dispatcher = request.getRequestDispatcher(String path);

RequestDispatcher

声明:public interface javax.Servlet. RequestDispatcher

定义接收来自客户端的请求并将它们发送到服务器上的任何资源(比如 Servlet、HTML 文件或 JSP 文件)的对象。Servlet 容器可创建 RequestDispatcher 对象,该对象被用作包装位于特定路径上的服务器资源或通过特定名称给定的服务器资源的包装器。

forward()控制页面跳转

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

该方法将请求从一个 Servlet 转发到服务器上的另一个资源(Servlet、JSP 文件或 HTML 文件)。此方法允许一个 Servlet 对请求进行初步处理,并使另一个资源生成响应。在将响应提交到客户端之前(在刷新响应正文输出之前),应该调用 forward。如果已经提交了响应,则此方法抛出 IllegalStateException。在转发之前,自动清除响应缓冲区中未提交的输出。

include()控制页面包含

include()用于在响应中包含其他资源(Servlet,JSP页面或HTML文件)的内容。即请求转发后,原先的Servlet还可以继续输出响应信息,转发到的Servlet对请求做出的响应将并加入原先Servlet的响应对象中

Servlet异常相关

在Servlet中有两种异常处理机制:声明式异常处理和程序式异常处理。

声明式异常处理

声明式异常处理是在web.xml文件中声明对各种异常的处理方法,这是通过<error-page>元素来声明的。<error-page>有两个子元素:子元素<error-code>指定HTTP协议的错误代码或<exception-type>指定异常的类;<location>指定用于响应HTTP错误代码的资源路径,该路径相对于Web应用程序根路径的位置,必须以(/)开头。

声明异常处理的方法:

  • 编写产生异常的Servlet
  • 编写错误处理页面
  • 配置<error-page>元素
  • 运行效果

程序式异常处理

在javax.Servlet包中定义了两个异常类,ServletExceptionUnavailableException

  • javax.Servlet.ServletException
    ServletException类定义了一个通用的异常,常用在init()service()doXX()方法中抛出异常,它提供了4个构造方法和1个获得异常原因的方法
  • javax.Servlet.UnavailableException
    UnavailableException类是ServletException类的子类,是当Servlet或者Filter暂时或永久不能用时,就会抛出这个异常。