說起Servlet的接收處理請求的方式,想必各位都並不陌生,如doGet、doPost、service...
那么他們的背后是如何執行?服務器怎么選擇知道的?我們就此來探討一下
本節案例的代碼奉上:
web.xml部分
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>one</servlet-name> <servlet-class>cn.arebirth.servlet.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>one</servlet-name> <url-pattern>/one</url-pattern> </servlet-mapping> </web-app>
JSP部分
<%-- Created by IntelliJ IDEA. User: Arebirth Date: 2019/8/17 Time: 15:00 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <form action="http://localhost:8080/ServletDemo_war_exploded/one" method="get"> <%--請求方式會改動--%> <label>Usercode:</label> <input type="text" name="name"> <br/> <label>Password:</label> <input type="password" name="password"> <br/> <input type="submit" value="Submit"> </form> </body> </html>
Servlet部分后續分析在具體展露。
下面我們來簡單的寫下具體用法,在做具體分析
doGet 相比不用說大家也都能見名知意,根據get的方式請求服務器
前端method:get請求
package cn.arebirth.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.doGet"); } }
結果
MyServlet:doGet
doPost 方式同上doGet,請求方式改變了
前端method:post請求
package cn.arebirth.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.doPost"); } }
結果:
MyServlet.doPost
service 接收請求
前端method:get or post方式皆可
package cn.arebirth.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.service"); } }
結果:
MyServlet.service
正題來了前方高能!
分析1:當我們以GET請求方式進行請求的時候,servlet中只有doPost會怎么樣?
package cn.arebirth.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.doPost"); } }
Result:
會報 HTTP Status 405-Method Not Allowed 405錯誤狀態碼 服務器不允許以此請求方式訪問
分析2:當我們以POST請求方式進行請求的時候,servlet中只有doGET會怎么樣?
Result:
同上,只是互換了一下還是會報405錯誤!
分析3:當我們以GET or POST請求方式進行請求的時候,servlet中只有doPost or doGet 和 service方法 那么它會執行誰?
//前端我們以get方式請求 package cn.arebirth.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.doGet"); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.service"); } }
Result:
結果是輸出
MyServlet.service
明明我們是以get方式進行請求的,而且servlet中含有doGet方法,為什么走的確實service??這是一個初學者的坑,讓我們來探究下吧!
底層實現:我們的服務器在接受到請求的時候,servlet首先會查找是否service方法,因為servlet只認識service,原因看下圖:
我們底層的servlet接口里面只有service接口!所以當我們的服務器接收到請求的時候首先會查找是否有service方法,如果沒有的話則會去父類中調用,
分析4:我們就上面分析3中可以得知,如果沒有servlet中沒有重寫service方法的話,那么它會調用父類的service方法,我們就此來分析
前端以get方式進行請求 package cn.arebirth.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.doGet"); } }
Result:
結果輸出的是:
MyServlet.doGet
我們來分析下執行原理:
首先請求達到這個servlet的時候,會查找本方法中是否有重寫了的service方法,沒有的話,將執行父類HttpServlet中的service方法首先會調用HttpServlet中一個重載的service方法,用於接收request和response,然后把request和response傳遞給另一個注的service重載的執行方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest)req; response = (HttpServletResponse)res; } catch (ClassCastException var6) { throw new ServletException("non-HTTP request or response"); } this.service(request, response); //這里吧request請求參數和response響應參數傳遞給另一個重載的方法並調用 } 另一個重載的執行方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod();//首先獲取請求方式 long lastModified;
//接着判斷請求方式, if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); //如果是GET請求方式就會通過多態的方式調用者個doGet方式, } 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")) {//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); } }
通過上面的底層代碼我們可以得知,它的底層實際上是不會直接調用我們servlet中寫的doGet或doPost方法,而是間接的通過service方法判斷請求方式,然后在通過多態的方式調用具體的請求,還是那句話因為它只認識service方法!!!!
分析4:當doGet or doPost和service方式同時存在,並且service方式中調用了父類的service方法,那么,它會得到什么結果??
前端以get方式進行請求 package cn.arebirth.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.doGet"); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet.service"); super.service(req, resp); } }
Result:
結果輸出的是:
MyServlet.service
MyServlet.doGet
這時有人就會說了,按照上面的例子來講,如果有service方法存在的話,那么不就不會調用doGet or doPost了嗎????
朋友,別忘了service方法里面還有 super.service(req,reps) 這句代碼!!
通過上面的底層分析,我們可以得知,它首先會執行我們重寫的service方法里面的代碼,然后遇見了super.service(req,reps) ,這句代碼是不是在調用父類HttpServlet的service方法??對吧。
所以他會根據響應的請求的方式,然后通過多態的方式調用了我們servlet中重寫的doGet or doPost方法,所以這樣就會一並執行啦!!
總結:
servlet執行的時候值認識service方法,如過我們自己寫的方法中沒有service方法的話,那么它就會逐級往上面找直到找到service方法然后去執行,如:我們繼承的HttpServlet抽象類,在它的里面找到了service方法之后,就會開始調用它的service方法,並根據響應的請求然后通過多態的方式調用相應的代碼!