这篇来学习实现Servlet的第三中方法,这种方法也是实际项目开发中采用的方法,通过实现HttpServlet类,这种方法也叫模板设计模式。
1. J2ee API 文档查看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
首先,看到这个报错是正常的,不是代码问题,因为在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方法之后就失去了模板方法的意义。这种模板方法的设计模式,就是我们实际项目中开发采用的方法。