­

Java-Servlet请求方式doXXX、service 具体分析

  • 2019 年 10 月 6 日
  • 筆記

说起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方法,并根据响应的请求然后通过多态的方式调用相应的代码!