这是HttpServlet的源码:
//HttpServlet是一个典型的模板类
public abstract class HttpServlet extends GenericServlet{
//这个service方法是实现的GenericServlet抽象类的抽象方法
//用户发送第一次请求的时候这个service会执行
//用户发送第N次请求的时候,这个service方法还是会执行。
//用户只要发送一次请求,这个service方法就会执行一次。
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
//将ServletRequest和ServeltResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}
//调用重载的service方法
this.service(request, response);
}
//这个service方法的两个参数都是带有Http的
//这个service是一个模板方法
//在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求方式
//这个请求方式最终可能是:""
//注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
//GET POST DELETE HEAD OPTIONS OOPTIONS TRACE
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//报405错误
String msg = lStrings.getString("http.method_get_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
/*
通过以上源代码分析:
假设前端发送的请求是get请求,后端程序员没有重写doGet方法
假设前端发送的请求是Post请求,后端程序员重写的方法是doGet
会发生什么呢?
发生405这样的一个错误,因为后端程序员写的子类实现继承HttpServlet后没有重写doGet方法,导致前端发送get请求后,该类执行service方法,service方法体中的request.getMethod方法识别到发送的是get方法,所以通过if语句算法会去执行doGet方法,但是后端程序员没有重写doGet方法,所以系统会执行父类的doGet方法,父类doGet方法的内容是报405错误。
405表示前端你的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。
通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.
怎么避免405错误呢?
后端重写doGet方法,前端一定要发get请求。
后端重写doPost方法,前端一定要发Post请求。
有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。这样,确实可以避免405的放生,但是不建议,405错误还是有用的,该报错的时候应该让他报错。
*/
HttpServlet类继承了GenericServlet抽象类,必须实现抽象类的service(ServletRequest,ServletResponse)方法,并在方法内写了很多必要的代码,大大简化了我们的编程。为了防止我们的类在继承HttpServlet类的时候重载他的service()方法,他很巧妙地写了一个空的service(HttpServletRequest,HttpServletResponse)方法,然后他在service(ServletRequest,ServletResponse)方法内调用了service(HttpServletRequest,HttpServletResponse)方法,这样service(ServletRequest,ServletResponse)方法执行的时候service(HttpServletRequest,HttpServletResponse)方法也会执行。这样我们的类重写service(HttpServletRequest,HttpServletResponse)方法既可以保证我们的代码可以在service(ServletRequest,ServletResponse)方法中运行,又可以保证service(ServletRequest,ServletResponse)中原来的代码不被覆盖。
同理,HttpServlet类在空的service(HttpServletRequest,HttpServletResponse)方法中加入了一些有用的代码,大大简化了我们的编程。但是也留了入口供我们使用,他又创建了doGet、doPost等空方法,并在service(HttpServletRequest,HttpServletResponse)方法中调用了这些方法,所以以后我们的类继承了HttpServlet类只需要重写doGet、doPost这些方法就可以了。更巧妙的是:为了保证我们重写了这些方法,HttpServlet类又在doGet、doPost等空方法中写了相关代码,代码内容如果执行就会报错。以后执行service()方法的时候必然会调用doGet、doPost等方法,如果我们不重写doGet、doPost这些方法,那会怎么样?根据继承关系,他会调用父类即HttpServlet类的doGet、doPost方法,一单调用就会报错,提示我们要重写这些方法!
这就是模板方法设计模式。