4.3.6 getLastModified 方法
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方法的规则如下:
- 当getLastModified返回-1时,service方法总会调用doGet方法。
- 当getLastModified返回正数时,如果HTTP请求消息头中没有If-Modified-Since字段,或者If-Modified-Since字段中的时间比Last-Modified字段中的时间早,service方法会调用doGet方法。浏览器在下次访问该Servlet时,If-Modified-Since字段的值就是上一次访问该Servlet的Last-Modified字段的值。
- 当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方法。因为如果是这样,浏览器可能会在一定时间内使用浏览器缓存的内容。