由于市面上的浏览器都会对已访问过的页面进行缓存,因此作为web开发人员都应该对浏览器的缓存有所了解。
在http协议中,浏览器对访问过的页面缓存后,它将会在以后访问该页面时,将会根据LastModified头字段指定的时间值生成If- Modified-Since头字段,作为缓存页面的最新更新时间。如果网页的最后修改时间比If-Modified-Since头字段指定的时间早的 话,web服务器就会请求的页面,如果自If-modified-Since指定的时间以来,网页内容没有被修改的话,服务器就会返回一个304响应头, 一次告诉浏览器继续使用已缓存的页面。
由于在动态网页程序中,网页内容随时在变化,因此我们要保证在每次的访问请求中都要返回最新的内容。例如一个网上商店卖某种产品,卖出一件,返回给 数据的网页当中显示产品的数量就要少一件,这时我们就需要返回最新的网页内容。但是浏览器不能根据网页的文件名或其他方法来确定是否所最新的。对于这种情 况浏览器可以根据在响应消息中是否包含Last-Modified字段头来进行处理。如果服务器的响应消息中没有包含Last-Modified字段头, 浏览器将会在每次访问该网页时候都会发出访问请求,否则会在浏览器程序首次运行访问该网页时才会发出访问请求,在后续的访问中都不会发出访问请求。在 servlet中,对Last-Modified头字段和If-Modified-Since头字段中都有相应的处理。在HttpServlet类中 getLastModified方法返回一个long型的时间值。getLastModified方法是一个回调方法,它由HttpServlet类中重 载的service方法来调用,然后会自动在响应消息中生成Last-Modified消息头字段。注意:HttpServlet类中的getLastModified方法的返回值是一个负数。
继承HttpServlet的servlet程序在接收到客户端的GET请求后,HttpServlet的重载service方法会先调用 getLastModified方法,根据这个方法的返回值来决定是否要调用doGet方法和生成Last-Modified头字段。主要有以下三种决定 方式:
1.如果getLastModified方法的返回值是一个负数的话,不管客户端的请求信息如何,service方法都会调用doGet方法生成响应信息返回给客户端。
2.如果getLastModified方法的返回值是一个正数,并且客户端的请求消息中没有包含If-Modified-Since头字段的(这 种情况是第一次访问该页面时)或者是请求消息中包含If-Modified-Since头字段,但是返回值比If-Modified-Since头字段指 定的时间新的话,则service方法调用doGet方法生成响应信息和Last-Modified消息头返回给客户端。
3.如果getLastModified方法的返回值是一个正数,并且返回值比客户端发出的请求消息中If-Modified-Since头字段指 定的时间值旧的话,那么service方法将不会调用doGet方法和生成Last-Modified头字段,而是返回一个304状态给客户端,表示让客 户端继续使用以前缓存的页面。
最后:我们知道当我们在servlet中的web.xml配置文件中有一个缺省servlet,它是用来当客户端浏览器访问一个静态页面时,默认使 用这个缺省servlet来处理这个静态页面(读取这个静态的内容,并将读取到数据返回给客户端浏览器)。这个缺省servlet将会默认生成一个 Last-Modified头字段返回给客户端。因此,在浏览器的一次启动运行期间,它只对某个html页面进行第一次访问时候才向服务器发出访问请求, 以后的后续访问都不会再向服务器发出访问请求了。对于某个html页面来说,由于浏览器在第一次访问该页面时紧跟着再进行后续的访问的几率很小,并且这种 情况造成的后果也不严重,因此为了提高访问效率,Tomcat的缺省servlet采用这种方式也是合理的。
HttpServlet中的service方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_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 / 1000 * 1000)) {
// 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);
}
}