web項目一旦部署完畢,項目中的圖檔、CSS以及JS基本上很少發生變動,那麼假如把這些元件緩存在浏覽器用戶端,而不再從伺服器上擷取,那麼網站的通路者在首次通路網站後,後續的請求将會大量減輕伺服器的請求壓力。這一舉動,帶來的性能提升,可以稱作完美!那麼如何做呢?那就是為元件添加Expires(期限)頭!
一、了解Expires頭
起初,在讀《高性能web建站指南》第三章“添加Expires頭”時,感覺很有壓力,因為不了解Expires頭是做什麼用的,是以就沒有心思去實踐該做法,就擱置了一段時間。然而今天心情很好,耐着性子在網上找一系列的資源,并且重讀這篇文章,實踐再三,終于搞定,特此分享,希望更多的朋友能夠看到,實踐到你的項目中,進而提升網站性能。
我提供幾篇文章大家讀一讀:
tomcat7官方doc中給出的Expires_Filter,可結合源碼進行分析。
浏覽器緩存詳細解析
HTTP1.1 協定
網站性能優化:cache-control設定詳解
有了這幾篇文章作為鋪墊,我想你基本上就可以了解Expires頭啦。
二、效果

對于一個添加了Expires頭的css來說,其請求的資訊如圖,多了max-age,以及Expires,這兩者之間的關系我也不再贅述,之前的文章中描述比較清楚。
三、實作方法
①、建立CacheControlFilter.java
package com.honzh.common.filter;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class CacheControlFilter implements Filter {
private FilterConfig config = null;
private static final String CACHE_CONTROL_BY_TYPE = "Cache-Control";
private static final String HEADER_EXPIRES = "Expires";
private static final Log log = LogFactory.getLog(CacheControlFilter.class);
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
boolean cacheControlSet = false;
for (Enumeration<String> names = config.getInitParameterNames(); names.hasMoreElements();) {
String headerName = (String) names.nextElement();
log.debug("參數名稱:" + headerName);
String value = config.getInitParameter(headerName);
log.debug("參數值:" + value);
// 說明包含了類型,此時要篩選符合條件的類型
String type = headerName.substring(CACHE_CONTROL_BY_TYPE.length()).trim();
log.debug("參數規定的類型:" + type);
String contentType = httpRequest.getHeader("Accept");
log.debug("請求内容類型為:" + contentType);
if (contains(contentType, ";")) {
// lookup content-type without charset match (e.g.
// "text/html")
String contentTypeWithoutCharset = substringBefore(contentType, ";").trim();
if (contentTypeWithoutCharset.indexOf(type) != -1) {
// 類型比對,再看值是不是max-age,如果是,将max-age設定為目前時間+1個月
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.MONTH, 1);
Date expirationDate = calendar.getTime();
httpResponse.setDateHeader(HEADER_EXPIRES, expirationDate.getTime());
String maxAgeDirective = "max-age="
+ ((expirationDate.getTime() - System.currentTimeMillis()) / 1000);
setControlHeader(httpResponse, "private," + maxAgeDirective);
cacheControlSet = true;
}
}
}
if (!cacheControlSet) {
setControlHeader(httpResponse, "private");
}
}
chain.doFilter(request, response);
// resp.setHeader("Expires", "Tue, 03 Jul 2001 06:00:00 GMT");
// resp.setDateHeader("Last-Modified", new Date().getTime());
// resp.setHeader("Cache-Control",
// "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
// resp.setHeader("Cache-Control", "private");
// resp.setHeader("Pragma", "no-cache");
}
private void setControlHeader(HttpServletResponse httpResponse, String cache_control) {
String cacheControlHeader = httpResponse.getHeader(CACHE_CONTROL_BY_TYPE);
String newCacheControlHeader = (cacheControlHeader == null) ? cache_control : cacheControlHeader + ", "
+ cache_control;
httpResponse.setHeader(CACHE_CONTROL_BY_TYPE, newCacheControlHeader);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
protected static boolean contains(String str, String searchStr) {
if (str == null || searchStr == null) {
return false;
}
return str.indexOf(searchStr) >= 0;
}
protected static String substringBefore(String str, String separator) {
if (str == null || str.isEmpty() || separator == null) {
return null;
}
if (separator.isEmpty()) {
return "";
}
int separatorIndex = str.indexOf(separator);
if (separatorIndex == -1) {
return str;
}
return str.substring(0, separatorIndex);
}
}
web.xml中我們為Expires設定了三種類型,分别為image、css、js,②中有詳細參數,這樣在filter中,我們将為這三種類型添加Expires、max-age。
至于類中提到的private,你可以關注我的另一篇文章gzip壓縮tomcat伺服器響應包,大幅提升web性能。
其他方面,代碼不複雜,主要的是做法,我将做法提供給大家。
②、web.xml
<filter>
<filter-name>cacheControlFilter</filter-name>
<filter-class>com.honzh.common.filter.CacheControlFilter</filter-class>
<init-param>
<param-name>Cache-Control image</param-name>
<param-value>max-age</param-value>
</init-param>
<init-param>
<param-name>Cache-Control text/css</param-name>
<param-value>max-age</param-value>
</init-param>
<init-param>
<param-name>Cache-Control application/javascript</param-name>
<param-value>max-age</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>cacheControlFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>