你们都知道IE等浏览器支持缓存,而且缓存策略可配置,这样能够极大提升访问服务器的性能,对于一些如JS脚本、CSS文件、图片等静态资源,已经缺省被支持,可是对于自定义Servlet,则默认状况下不支持缓存,这javascript
是什么缘由呢?如何让自定义Servlet也支持缓存呢?
首先来了解下浏览器的缓存机制:对于全部的HTTP Get请求,浏览器会先检查客户端本地缓存是否存在,若是存在,则会在请求头中包含If-Modified-Since头信息,这个Header的值是本地缓存中资源的最后修改时间,服务端经过访问这个头信息和服务器上资源的最后修改时间进行比较,若是服务器端时间更新,则表示资源被修改了,须要从新下载,会调用doGet方法看成正常的Get请求处理,若是服务器端资源最后修改时间不大于客户端的最后修改时间,则表示资源没有修改,直接返回一个304的状态码,表示请浏览器使用本身缓存中的就好了,从而实现了缓存,减小了网络传输,提升了性能。
可是不是这样的处理在每次请求时都会作呢?答案是否认的!其实这种处理是每一个浏览器进程对相同的请求只会作一次,第一次检查完版本后对于后续相同的请求根本不会向服务器发任何请求,直接从客户端缓存中取资源。这样就更进一步提升了性能。
好了,明白了浏览器的缓存原理后,咱们来看下如何让咱们本身写的Servlet也可以被浏览器识别并缓存呢?其实这种处理在Servlet的基类中已经处理好了,实现者只须要提供一个最后修改时间的机制就好了。先来看下java
基类(HttpServlet)中是如何实现的(反编译代码):浏览器
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if(method.equals("GET")) { long lastModified = getLastModified(req); if(lastModified == -1L) { doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader("If-Modified-Since"); if(ifModifiedSince < (lastModified / 1000L) * 1000L) { maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(304); } } } else ...... }
从上面的代码实现就能够看出,取最后修改时间是调用getLastModifyed方法,这是一个保护的方法,定义以下:
protected long getLastModified(HttpServletRequest req)
{
return -1L;
}
因此要想Servlet可以支持浏览器缓存,只须要把这个方法从新实现为返回正确的最后修改时间就好了。
举个例子:
咱们实现一个Servlet,产生一个服务器端的Java类的客户端JavaScript代理,这被一些Ajax框所使用,
整个类以下所示,覆盖实现了getLastModifyed方法:缓存
public class RemoteServlet extends HttpServlet { public RemoteServlet() { } /** * 记录类的最近访问时间. */ private static final Map DATEMAP=new HashMap(); /* * (non-Javadoc) * * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/x-javascript;charset=GBK"); String path = request.getServletPath(); int index = path.lastIndexOf('/'); path = path.substring(index + 1); PrintWriter writer = response.getWriter(); String scriptStr = ""; try { //若是不存在,则添加当前时间 if(!DATEMAP.containsKey(path)){ DATEMAP.put(path,new Date()); } scriptStr = ClassToRemoteUtil.ClassToRemote(path); } catch (Exception e) { if(log.isErrorEnabled()){ log.error(e.getMessage(),e); } scriptStr="alert('产生远程服务代理"+path+"失败:"+e.getMessage()+"');"; } writer.write(scriptStr); } /** * {@inheritDoc} * @see javax.servlet.http.HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest) */ protected long getLastModified(HttpServletRequest req) { String path = req.getServletPath(); int index = path.lastIndexOf('/'); path = path.substring(index + 1); Date lastModifyDate=(Date)DATEMAP.get(path); if(lastModifyDate==null){ lastModifyDate=new Date(); DATEMAP.put(path, lastModifyDate); } return lastModifyDate.getTime(); } }
这样处理之后,这个Servlet的全部请求都可以被浏览器缓存支持。这个Servlet处理的请求为http://xxxx.xxxx.xxx.xx:port/xxxx/classfullname.ro,
则全部的.ro请求均可以用到浏览器缓存,从而提升性能。服务器