JavaWeb学习-Servlet系列-5-继承HttpServlet类-模板方法设计模式

这篇来学习实现Servlet的第三中方法,这种方法也是实际项目开发中采用的方法,通过实现HttpServlet类,这种方法也叫模板设计模式。

 

1. J2ee API 文档查看HttpServlet类

JavaWeb学习-Servlet系列-5-继承HttpServlet类-模板方法设计模式

需要知道这几点:

1)这个HttpServlet类实现了前面文章介绍的 GenericServlet 类,是这个类的子类。

2)这个HttpServlet类新加了一些doXXX方法,例如doPost和doGet,Post和Get我们知道是http请求方法。

3)继承HttpServlet的类必须需要实现上面截图列出几个方法的一个,待会我们试一试不重新一个方法试试。

4)几乎没有理由重写service方法,我们在文章最后来解释。

 

2. 写一个类继承HttpServlet类

写一个ServletDemo3.java类,继承HttpServlet,这个继承之后默认不需要在子类中重写相关方法的,例如不需要重写service()方法,因为在HttpServlet中已经帮我们重写了servicel方法。

package com.anthony.servlet;

import javax.servlet.http.HttpServlet;

public class ServletDemo3 extends HttpServlet {

}

在web.xml中为这个servlet类添加一个map映射

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <!-- 创建一个Servlet实例 -->
  <servlet>
  	<servlet-name>servletDemo1</servlet-name>
  	<servlet-class>com.anthony.servlet.ServletDemo1</servlet-class>
  </servlet>
  
  <!-- 给当前Servlet提供/映射一个可供客户端访问的URI -->
  <servlet-mapping>
  	<servlet-name>servletDemo1</servlet-name>
  	<url-pattern>/demo</url-pattern>
  	<!-- 这个/表示,当前应用下路径,所以/demo实际表示 http://localhost:8080/Servlet01/demo -->
  </servlet-mapping>
  
  <servlet>
  	<servlet-name>servletDemo3</servlet-name>
  	<servlet-class>com.anthony.servlet.ServletDemo3</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>servletDemo3</servlet-name>
  	<url-pattern>/demo3</url-pattern>
  </servlet-mapping>
  
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  
</web-app>

重新发布到tomcat服务器,浏览器输入 http://localhost:8080/Servlet01/demo3

JavaWeb学习-Servlet系列-5-继承HttpServlet类-模板方法设计模式

首先,看到这个报错是正常的,不是代码问题,因为在HttpServlet类中自己实现的service方法就是这样代码逻辑实现的,下面我们看源码来解释。

 

4.阅读HttpServlet类源码

这里我们来找一找我们浏览器为社么要给显示一个“HTTP method GET is not supported by this URL”的错误。在Eclipse上点击HttpServlet进入到这个类的源码,找到下面重写service这个方法。

 @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }

我们看到@Override就明白,HttpServlet类重写了Servlet接口的service方法。所以在运行ServletDemo3的实例过程中,本来是执行servlet接口的service方法,但是由于HttpServlet重写这个方法,所以变成执行这个子类重写的service方法。HttpServletRequest是ServletRequest的子类,同理HttpServletResponse。先是在try语句块中,把父类带来参数req和res强制转换成HttpServletRequest和HttpServletResponse对象。如果不是http请求或者响应就报异常。关键最后还是调用了一个service()方法,这个service是HttpServlet中自己新写的一个重载方法。所以,我们去看这个重载方法的源码。

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
    }

我上面删除了其他Http请求类型的代码,我们只看doGet和doPost。一上来,执行 String method = req.getMethod(); 来获取请求消息行中的请求方法是什么。下面继续走,如果这个请求是Get方法,就去执行doGet(req, resp);, 下面我们跳转到doGet()方法去看源码。

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

在这个doGet方法中,先获取协议是不是http/1.1, 然后判断,如果是http/1.1,就执行resp.sendError()的方法,接下来我们来看看这个msg的内容是什么。我们找到apache-tomcat-8.5.40-src.zip文件(前面文章解释过去tomcat网站下载这个文件),解压到本地,然后找到这个文件路径去打开资源文件apache-tomcat-8.5.40-src\java\javax\servlet\http


http.method_get_not_supported=HTTP method GET is not supported by this URL
http.method_post_not_supported=HTTP method POST is not supported by this URL
http.method_put_not_supported=HTTP method PUT is not supported by this URL
http.method_delete_not_supported=Http method DELETE is not supported by this URL

第一行这个字符串就是我们在浏览器中出现的字符串,提醒以下,这里获取资源文件的key和value就是使用了ResourceBundle这个类。

 

5. 重写doGet和doPost方法

上面我们实验过继承HttpServlet类但是不重写任何方法的效果,现在我们知道了浏览器为什么报这个错误,所以需要重写几个方法,让打印消息改成我们自己的msg。

package com.anthony.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletDemo3 extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("************Run doGet************");
		
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("************Run doGet************");
	}
	
}

重新发布到tomcat服务区,浏览器打开页面,就不会报错,显示一个空白页,而且Eclipse控制台输出“Run doGet”。 因为我们写的类重写了doGet和doPost方法,所以运行过程,运行到HttpServlet中doGet()方法就会调用我们重写的方法。

 

5. 为什么不能重写service方法

因为HttpServlet类中的serivce方法是实现了模板方法,如果你写Servlet类继承了HttpServlet类,那么重写servic方法之后就失去了模板方法的意义。这种模板方法的设计模式,就是我们实际项目中开发采用的方法。