你們都知道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請求均可以用到瀏覽器緩存,從而提升性能。服務器