• 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》

  • 相关阅读:
    C# is 与 as 运算符
    C# dynamic类型
    C# 数组
    C# 泛型
    C# 事件
    C# 委托
    C# DateTime类,TimeSpan类
    C# 获取当前应用程序的绝对路径支持asp.net
    C# 父子类_实例_静态成员变量_构造函数的执行顺序
    C# System.Uri类_获取Url的各种属性_文件名_参数_域名_端口等等
  • 原文地址:https://www.cnblogs.com/tuyang1129/p/11644881.html
Copyright © 2020-2023  润新知