4.3.6 getLastModified 方法

优质
小牛编辑
130浏览
2023-12-01

HTTP响应消息头有一个Last-Modified字段,这个字段表示服务器内容最新修改时间。如果请求消息头中包含If-Modificed-Since字段,并且该字段的时间比Last-Modified字段的时间早。或是请求消息头中没有If-Modificed-Since字段。service方法就会调用doGet方法来重新获得服务端内容。但这有一个前提,就是getLastModified方法必须返回一个正数。但在默认情况下,getLastModified方法返回-1。因此,service方法调用用doGet方法的规则如下:

l 当getLastModified返回-1时,service方法总会调用doGet方法。

2 当getLastModified返回正数时,如果HTTP请求消息头中没有If-Modified-Since字段,或者If-Modified-Since字段中的时间比Last-Modified字段中的时间早,service方法会调用doGet方法。浏览器在下次访问该Servlet时,If-Modified-Since字段的值就是上一次访问该Servlet的Last-Modified字段的值。

3 当getLastModified方法返回正数时,如果If-Modified-Since字段中的时间比Last-Modified字段中的时间晚,或者这两个字段的时间相同,service将不会调用doGet方法,而是向浏览器反回一个304(Not Modified)状态码来通知浏览器继续使用以前缓冲过的内容。

下面是HttpServlet类中service方法的关于上面规则的实现代码:

protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET))
{
    //  获得getLastModified方法的返回值
        long lastModified = getLastModified(req);
        //  当lastModified为-1时,调用doGet方法
        if (lastModified == -1)
    {
            doGet(req, resp);
        }
else
{
    //  获得If-Modified-Since字段值,并将其转换成long型时间
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            //  当If-Modified-Since字段中的时间比Last-Modified字段中的时间早时,
//  调用doGet方法
            if (ifModifiedSince < (lastModified / 1000 * 1000))
{
    // 设置Last-Modified字段值
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            }
else
{
    //  返回304错误 
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }
        ... ...
}
}

 

下面的例子演示了如何通过getLastModified方法控制浏览器是否使用被缓存的内容。

HTTP响应消息头有一个Last-Modified字段,这个字段表示服务器内容最新修改时间。如果请求消息头中包含If-Modificed-Since字段,并且该字段的时间比Last-Modified字段的时间早。或是请求消息头中没有If-Modificed-Since字段。service方法就会调用doGet方法来重新获得服务端内容。但这有一个前提,就是getLastModified方法必须返回一个正数。但在默认情况下,getLastModified方法返回-1。因此,service方法调用用doGet方法的规则如下:

  1. 当getLastModified返回-1时,service方法总会调用doGet方法。
  2. 当getLastModified返回正数时,如果HTTP请求消息头中没有If-Modified-Since字段,或者If-Modified-Since字段中的时间比Last-Modified字段中的时间早,service方法会调用doGet方法。浏览器在下次访问该Servlet时,If-Modified-Since字段的值就是上一次访问该Servlet的Last-Modified字段的值。
  3. 当getLastModified方法返回正数时,如果If-Modified-Since字段中的时间比Last-Modified字段中的时间晚,或者这两个字段的时间相同,service将不会调用doGet方法,而是向浏览器反回一个304(Not Modified)状态码来通知浏览器继续使用以前缓冲过的内容。

下面是HttpServlet类中service方法的关于上面规则的实现代码:

protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET))
{
    //  获得getLastModified方法的返回值
        long lastModified = getLastModified(req);
        //  当lastModified为-1时,调用doGet方法
        if (lastModified == -1)
{
            doGet(req, resp);
        }
else
{
    //  获得If-Modified-Since字段值,并将其转换成long型时间
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            //  当If-Modified-Since字段中的时间比Last-Modified字段中的时间早时,
//  调用doGet方法
            if (ifModifiedSince < (lastModified / 1000 * 1000))
       {
        // 设置Last-Modified字段值
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            }
else
{
    //  返回304错误 
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }
        ... ...
}
}

 

下面的例子演示了如何通过getLastModified方法控制浏览器是否使用被缓存的内容。

例子 : 用getLastModified方法控制浏览器使用缓冲内容

1. 实例说明

本程序通过使getLastModified方法返回不同的值来决定service方法是否执行doGet方法。如果service方法不执行doGet方法,虽然Servlet被成功调用,但是并没有执行doGet方法,因此,Servlet并没有返回新的服务端内容。

2. 编写Servlet类

在CacheServlet类中覆盖了getLastModifed方法,并返回了当前的时间(以毫秒为单位),代码如下:

public class CacheServlet extends HttpServlet
{
    //  覆盖HttpServlet类的getLastModified方法
    protected long getLastModified(HttpServletRequest req)
    {
        long now = System.currentTimeMillis();
        //  返回当前时间
        return System.currentTimeMillis();
    }
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException
    {
        response.getWriter().println(System.currentTimeMillis());
        System.out.println(request.getHeader("if-Modified-Since"));
    }
}

CacheServlet类的配置代码如下:

</servlet-mapping>
    <servlet>
    <servlet-name>CacheServlet</servlet-name>
    <servlet-class>chapter4.CacheServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>CacheServlet</servlet-name>
    <url-pattern>/CacheServlet</url-pattern>
</servlet-mapping>

3. 通过测试程序来观察客户端输出的时间是否变化

在IE地址栏中输入如下的URL:

http://localhost:8080/demo/CacheServlet

读者会看到在IE中输出当前服务器的时间(毫秒值)。当使用F5不断刷新页面时,会看到这个值也在不断地变化。从而可以断定,CacheServlet的doGet方法被调用了。现在将服务器的时间往回调整一下(如调整到前一天),再次按F5刷新,这时页面的毫秒时间就不会再发生变化了。这是因为目前的服务器时间比HTTP请求消息头的If-Modifed-Since字段值指定的时间早,因此,service就不会调用doGet方法了,当然也就不会输出当前的服务器时间了。实际上,浏览器使用的是被缓存的内容。

读者可以从IE的缓存目录(C:\Documents and Settings\Administrator\Local Settings\Temporary Internet Files)找到CacheServlet,并将其删除,再次按F5刷新页面。就会看到页面的时间变化了,当再次按F5时,又不变化了。这是因为当把IE的相关缓存删除后,由于IE找不到缓存内容,因此,无法设置HTTP请求消息头的If-Modified-Since段,这也正符合上述第二个规则中调用doGet方法的条件。因此,第一次刷新页面时CacheServlet返回了时间信息,当再次刷新页面时,则CacheServlet又被缓冲了,所以当再次发送HTTP请求时,If-Modifed-Since字段中的时间和Last-Modified字段中的时间相等,因此,service方法会返回304状态码,这时IE就会使用被缓存的CacheServlet。

4.  程序总结

在service方法中只有doGet方法考虑了If-Modified-Since和Last-Modified字段,其他的方法,如doPost,并不涉及到这两个字段,因此,除了doGet方法,其他的doXxx方法总会被调用。

对于需要实时获得返回结果的Servlet,我并不建议覆盖getLastModified方法。因为如果是这样,浏览器可能会在一定时间内使用浏览器缓存的内容。