HttpServlet中service方法的源码解读
前言
最近在看《Head First Servlet & JSP》这本书, 对servlet有了更加深入的理解。今天就来写一篇博客,谈一谈Servlet中一个重要的方法——service方法。
介绍
当浏览器对servlet发起请求时,web容器会开启一个新的线程,或者是从线程池中分配一个线程,并调用servlet的service()方法,这个方法不需要程序员编写,而是继承自父类HttpServlet(当然,servlet不一定继承HttpServlet,也可以是实现其它协议的servlet类,但是大部分情况是Http协议);
在service()方法中,会查看请求的类型(Get,Post......),根据请求的类型,调用servlet中对应的方法,如doGet()、doPost()......下面就来看看HttpServlet类中service()方法的源码。
源码
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》