前言

    最近在看《Head First Servlet & JSP》这本书, 对servlet有了更加深入的理解。今天就来写一篇博客,谈一谈Servlet中一个重要的方法——service方法


介绍

    当浏览器对servlet发起请求时,web容器会开启一个新的线程,或者是从线程池中分配一个线程,并调用servlet的service()方法,这个方法不需要程序员编写,而是继承自父类HttpServlet(当然,servlet不一定继承HttpServlet,也可以是实现其它协议的servlet类,但是大部分情况是Http协议);

    在service()方法中,会查看请求的类型(Get,Post……),根据请求的类型,调用servlet中对应的方法,如doGet()、doPost()……下面就来看看HttpServlet类中service()方法的源码。


源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();

if (method.equals(METHOD_GET)) { //若为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 = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// 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_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);

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

} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);

} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);

} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);

} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);

} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//

String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);

resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

源码解读

对于Get类型的请求

    在上面的源代码中,若请求是Get类型的,那service()方法首先调用了getLastModified()方法,获取了一个值赋给lastModified变量,这个东西是什么呢?

    首先要知道一个东西,叫做If-Modified-Since(源码的注释中也有提到),这是Http请求的一个请求头标签,记录的是你现在请求的文件,在上一次你请求的时候,服务器上最后修改它的时间。所以,前提是你之前已经请求过这个文件,浏览器中存有这个页面的缓存,才有这个值。而源码中的getLastModified()方法,就是用来获取这个值的。

    对于第一次请求的文件,浏览器请求中是没有If-Modified-Since的,所以在上面的源码中,若是第一次请求,getLastModified()返回的是-1,表示是第一次请求,则直接调用doGet方法,获取服务器中的文件,而这时在浏览器中,也获得了请求的文件在服务器中最后被修改的时间;

    而对于不是第一次的请求,浏览器会将If-Modified-Since通过请求发送到服务器,service()方法调用getLastModified()方法获取到了发送来的这个值;然后它要判断一件事,那就是在你上次请求到这次请求之间,这个文件在服务器上是否被修改了,若被修改,则调用doGet(),重新获取一次,若没有被修改,则直接使用你浏览器中这个文件的缓存。

    那service方法是怎么做到这个操作的呢。看上面的源代码,若getLastModified()方法获取到的值不是-1,则表示你之前请求过这个页面,并在浏览器中有缓存。然后,service()方法调用req.getDateHeader(HEADER_IFMODSINCE),获取服务器上,你请求文件的最后修改时间,并与你传来的最后修改时间进行比较(两者都是long类型,表示时间的毫秒值),若服务器上的最后修改时间 > 你传来的最后修改时间,则表示在你上次请求之后,这个文件被修改过,所以不能直接使用缓存,于是service()方法调用doGet()方法重新获取此文件,而浏览器的缓存以及If-Modified-Since也将得到更新;若服务器上的最后修改时间 == 你传来的最后修改时间,表示这个文件没有被修改,service()方法调用resp.setStatus()方法 ,为响应设置状态码304(HttpServletResponse.SC_NOT_MODIFIED == 304),告诉浏览器可以直接使用缓存。

对于Post类型的请求

    对于Post类型的请求,service()方法都是直接调用doPost()方法,因为Post请求在标准中被规定用来对服务器中的内容进行修改,所以没有必要考虑缓存(个人理解)。当然,还有其它6种请求,但是基本上不用,我也不是很懂,这里就不说了。


源码下载地址

http://www.java2s.com/Code/JarDownload/javax.servlet/javax.servlet-api-3.0.1-sources.jar.zip


参考文献

https://www.cnblogs.com/moxiaotao/p/9670109.html

https://www.2cto.com/kf/201705/638441.html

《Head First Servlet & JSP》