4.2.1 Servlet程序的层次关系
Servlet是J2EE规范的一个重要组成部分。而在J2EE中的所有规范(EJB、Servlet、JSP等)都使用了接口(interface)来定义,由于类在实现接口时。必须实现接口中定义的所有方法,因此,使用接口来定义规范可以强制规范的实现者必须按着统一的标准来工作。总而言之,在面向对象程序设计领域,规范可以被看作是接口(也可能会有抽象类)的集合。
既然Servlet也是规范,那么我们自然会想到,Servlet也是由一组接口来定义的。实际上,Servlet规范由一个名为Servlet的接口和其他相关的接口组成。所有的Servlet类必须实现Servlet接口。这些接口可以在<Tomcat安装目录>\lib\servlet-api.jar文件中找到。Servlet接口的定义如下:
package javax.servlet;
import java.io.IOException;
public interface Servlet
{
// 初始化Servlet的方法
public void init(ServletConfig config) throws ServletException;
// 获得Servlet配置的方法
public ServletConfig getServletConfig();
// 处理所有HTTP请求的方法
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
// Servlet对象实例在销毁之前调用的方法
public void destroy();
}
读者从上面的代码可以看出,Servlet共定义了五个方法,这就意味着每编写一个Servlet程序,就必须实现五个方法。但这些方法在实际工作中并不都会用到,因此,每次重复地实现这些方法就会增加很多不必要的工作量。
为了减少编写Servlet类的工作量,在servlet-api.jar包中提供了一个GenericServlet抽象类,GenericServlet实现了Servlet接口中除了service方法外的其他四个方法。因此,现在所有的Servlet类只需要从GenericServlet继承就可以了,而且只需要实现service方法。
service方法是Servlet的核心方法。客户端向服务端发送的各种HTTP请求(GET、POST、HEAD等)都要由service方法处理。service方法有两个参数:req和res。这两个参数的类型分别是:ServletRequest和ServletResponse。它们都是接口,实现这两个接口的类分别封装了InputStream和OutputStream(这两个流是从客户端的Socket中获得)。也就是说,service方法通过req从客户端获得请求消息,通过res向客户端返回响应消息。
虽然只需要实现一个方法,但仍然有很多问题需要编写大量的代码解决,如将不同的HTTP请求方法(GET、POST、HEAD等)分开处理。因此,在servlet-api.jar包中提供了另一个抽象类HttpServlet。这个类从GenericServlet继承,并实现了service方法。在HttpServlet类中通过service方法将处理不同的HTTP请求方法的工作交由相应的方法处理,如doGet、doPost、doHead等。下面是HttpServlet类中doGet方法和service方法的代码片段:
package javax.servlet.http;
public abstract class HttpServlet extends GenericServlet implements
java.io.Serializable
{
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// 获得HTTP协议版本
String protocol = req.getProtocol();
// 定义不支持该方法的异常信息
String msg = lStrings.getString("http.method_get_not_supported");
// 根据HTTP协议版本不同而抛出不同的异常
if(protocol.endsWith("1.1"))
{
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
}
else
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
// 处理所有请求的service方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
// 获得HTTP请求方法
String method = req.getMethod();
// 处理HTTP GET方法
if(method.equals(METHOD_GET))
{
doGet(req, resp);
}
// 处理HTTP HEAD方法
else if(method.equals(METHOD_HEAD))
{
... ...
}
... ...
}
}
从上面的代码片段可以看出,在HttpServlet类中doGet方法有一个默认的实现,如果在HttpServlet的子类中不覆盖doGet方法,通过HTTP GET方法发送请求,就会抛出不支持HTTP GET方法的异常。在service方法中根据从客户端获得的HTTP请求方法名(method变量)来决定调用哪一个doXxx方法(doGet、doPost、doHead等)。
有了HttpServlet类,在编写Servlet类就轻松多了,一个Servlet类只需要从HttpServlet类继承,而无需编写一行代码就可以实现一个空的Servlet程序(虽然HttpServlet是抽象类,但在HttpServlet中并没有一个抽象方法,因此,这个类除了不能实例化外,和普通类没什么区别)。因此,Servlet程序一般都继承HttpServlet类。Servlet类的层次结构如图4.2所示。
图4.2 Servlet类的层次结构图
图4.2中虚线框表示接口、长虚线框表示抽象类,实线框表示类。