package com.baidu.global.mobile.hao123.nantianmen.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <pre>
* 设置HTTP Response Header中关于Cache的控制头——Cache-Control和Expires(For HTTP 1.0)。
*
* Cache-Control
* public = true | false
* max-age = delta-seconds (0 ==> no-cache | no-store )
* s-maxage = delta-seconds.
* must-revalidate = true | false
* no-transform = true | false
* Expires = 日期字符串,格式为yyyy-MM-dd HH:mm:ss。并且自动推算匹配的max-age。
*
* 说明:使用annotation意味着只能支持静态配置。而不是动态的信息。所以如果value值需要动态设置的话就需要程序自己在handler method中处理了。
* 比如 Last-Modified和Etag。max-age和Expires有可能也需要动态,这种情况下就不能使用这个annotation。
* 可以考虑使用JAX-RS框架[HTTP Header Caching in JAX-RS](https://devcenter.heroku.com/articles/jax-rs-http-caching)。
* </pre>
*
* @author zhengzhibin
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheControl {
/**
* public和private都是关键字。。
*
*/
boolean isPublic() default true;
/**
* max-age = delta-seconds (0 ==> no-cache | no-store )。
*
*/
int maxAge() default -1;
/**
* s-maxage = delta-seconds.
*/
int sMaxAge() default -1;
/**
* must-revalidate = true | false
*/
boolean mustRevalidate() default false;
/**
* proxy-revalidate = true | false
*/
boolean proxyRevalidate() default false;
/**
* no-transform = true | false
*/
boolean noTransform() default true;
/**
* <pre>
* Expires = 日期字符串,并且自动推算匹配的max-age。
* HTTP标准是RFC 1123 date format, e.g., Thu, 01 Dec 1994 16:00:00 GMT.
* public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
* 但是这个格式对于我们中国人非常不友好,还是改成这个格式方便些:yyyy-MM-dd HH:mm:ss。
*
* </pre>
*/
String expires() default "";
}
package me.arganzheng.study.builder.common;
import org.apache.commons.lang.StringUtils;
/**
* <pre>
* Cache-Control Header
*
* NOTE: 这个不会顺便增加Expires头。
* </pre>
*
* @author zhengzhibin
*
*/
public class CacheControlHeader {
public enum CacheType {
PUBLIC, PRIVATE;
}
/**
* public | private
*
*/
CacheType cacheType = null;
/**
* max-age = delta-seconds (0 ==> no-cache | no-store )。
*
*/
boolean noCache;
boolean noStore;
int maxAge = -1;
/**
* s-maxage = delta-seconds.
*/
int sMaxAge = -1;
/**
* must-revalidate = true | false
*/
boolean mustRevalidate = false;
/**
* proxy-revalidate = true | false
*/
boolean proxyRevalidate = false;
/**
* no-transform = true | false
*/
boolean noTransform = false;
private CacheControlHeader() {
}
public static Builder newBuilder() {
return Builder.create();
}
public CacheType getCacheType() {
return cacheType;
}
public boolean isNoCache() {
return noCache;
}
public boolean isNoStore() {
return noStore;
}
public int getMaxAge() {
return maxAge;
}
public int getsMaxAge() {
return sMaxAge;
}
public boolean isMustRevalidate() {
return mustRevalidate;
}
public boolean isProxyRevalidate() {
return proxyRevalidate;
}
public boolean isNoTransform() {
return noTransform;
}
public String stringValue() {
StringBuilder sb = new StringBuilder();
if (cacheType == CacheType.PUBLIC) {
append(sb, "public");
} else if (cacheType == CacheType.PRIVATE) {
append(sb, "private");
}
if (mustRevalidate) {
append(sb, "must-revalidate");
}
if (noTransform) {
append(sb, "no-transform");
}
if (proxyRevalidate) {
append(sb, "proxy-revalidate");
}
if (maxAge == 0) {
append(sb, "max-age=0");
noCache = noStore = true;
} else if (maxAge > 0) {
append(sb, "max-age=" + maxAge);
}
if (sMaxAge == 0) {
append(sb, "s-maxage=0");
noCache = noStore = true;
} else if (sMaxAge > 0) {
append(sb, "s-maxage=" + sMaxAge);
}
if (noCache || noStore) {
append(sb, "no-cache,no-store");
}
return sb.toString();
}
private StringBuilder append(StringBuilder sb, String str) {
if (sb == null) {
sb = new StringBuilder();
sb.append(str);
} else {
String s = StringUtils.trimToEmpty(sb.toString());
if (StringUtils.isEmpty(s) || s.charAt(s.length() - 1) == ',') {
sb.append(str);
} else {
sb.append(',').append(str);
}
}
return sb;
}
package com.baidu.global.mobile.hao123.nantianmen.interceptor;
import java.text.ParseException;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import com.baidu.global.mobile.hao123.nantianmen.annotation.CacheControl;
import com.baidu.global.mobile.hao123.nantianmen.common.CacheControlHeader;
import com.baidu.global.mobile.hao123.nantianmen.common.CacheControlHeader.CacheType;
/**
* 处理@CacheControl注解
*
* @author zhengzhibin
*
*/
public class CacheControlInterceptor extends HandlerInterceptorAdapter {
public static final String EXPIRES_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
private static final Logger logger = Logger.getLogger(CacheControlInterceptor.class);
/**
* 根据@CacheControl注解设置对应的HTTP Response Header
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
CacheControl cacheControlAnnotation = method.getMethodAnnotation(CacheControl.class);
if (cacheControlAnnotation == null
|| StringUtils.isNotEmpty(response.getHeader("Cache-Control"))) {
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Response with CacheControl Set!");
}
CacheControlHeader.Builder cc = CacheControlHeader.newBuilder();
if (cacheControlAnnotation.isPublic()) {
cc.setCacheType(CacheType.PUBLIC);
} else {
cc.setCacheType(CacheType.PRIVATE);
}
Date expiresDate = null;
if (cacheControlAnnotation.mustRevalidate()) {
cc.setMustRevalidate(true);
}
if (cacheControlAnnotation.noTransform()) {
cc.setNoTransform(true);
}
if (cacheControlAnnotation.proxyRevalidate()) {
cc.setProxyRevalidate(true);
}
if (cacheControlAnnotation.maxAge() >= 0) {
cc.setMaxAge(cacheControlAnnotation.maxAge());
}
if (cacheControlAnnotation.sMaxAge() >= 0) {
cc.setsMaxAge(cacheControlAnnotation.sMaxAge());
}
if (StringUtils.isNotEmpty(cacheControlAnnotation.expires())) {
try {
expiresDate = DateUtils.parseDate(cacheControlAnnotation.expires(),
new String[] { EXPIRES_PATTERN, PATTERN_RFC1123 });
Date now = new Date();
long age = expiresDate.getTime() - now.getTime();
long maxAge = age / 1000;
if (cacheControlAnnotation.maxAge() < 0) { // maxAge not set
cc.setMaxAge(Long.valueOf(maxAge).intValue());
}
if (cacheControlAnnotation.sMaxAge() < 0) { // sMaxAge not
// set
cc.setsMaxAge(Long.valueOf(maxAge).intValue());
}
} catch (ParseException e) {
logger.error("parse expires date failed! Expired Date = "
+ cacheControlAnnotation.expires());
}
}
String value = cc.build().stringValue();
if (StringUtils.isNotBlank(value)) {
response.addHeader("Cache-Control", value);
}
if (expiresDate != null) {
response.addHeader("Expires",
FastDateFormat.getInstance(PATTERN_RFC1123).format(expiresDate));
}
}
}
}
/**
* <pre>
* Builder for CacheControlHeader.
*
* 这里没有处理字段是否有被显示设置问题。可以考虑每个字段增加一个标志字段,表面是否被显示设值。
* </pre>
*
* @author zhengzhibin
*
*/
public static class Builder {
private CacheControlHeader cacheControlHeader;
public static Builder create() {
return new Builder();
}
private Builder() {
this.cacheControlHeader = new CacheControlHeader();
}
public Builder setCacheType(CacheType cacheType) {
cacheControlHeader.cacheType = cacheType;
return this;
}
public Builder setNoCache(boolean noCache) {
cacheControlHeader.noCache = noCache;
return this;
}
public Builder setNoStore(boolean noStore) {
cacheControlHeader.noStore = noStore;
return this;
}
public Builder setMaxAge(int maxAge) {
cacheControlHeader.maxAge = maxAge;
return this;
}
public Builder setsMaxAge(int sMaxAge) {
cacheControlHeader.sMaxAge = sMaxAge;
return this;
}
public Builder setMustRevalidate(boolean mustRevalidate) {
cacheControlHeader.mustRevalidate = mustRevalidate;
return this;
}
public Builder setProxyRevalidate(boolean proxyRevalidate) {
cacheControlHeader.proxyRevalidate = proxyRevalidate;
return this;
}
public Builder setNoTransform(boolean noTransform) {
cacheControlHeader.noTransform = noTransform;
return this;
}
public CacheControlHeader build() {
return cacheControlHeader;
}
}
}
package com.baidu.global.mobile.hao123.nantianmen.web;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.baidu.global.mobile.hao123.nantianmen.common.CacheControlHeader;
import com.baidu.global.mobile.hao123.nantianmen.common.CacheControlHeader.CacheType;
import com.baidu.global.mobile.hao123.nantianmen.dto.BabyNews;
import com.baidu.global.mobile.hao123.nantianmen.dto.BabyRanking;
/**
*
* @author zhengzhibin
*
*/
@Controller
@RequestMapping("/football")
public class FootballBabyRankingController {
@RequestMapping(value = "/baby/rank", method = RequestMethod.GET)
// @CacheControl(isPublic = true, maxAge = 300, sMaxAge = 300)
public String footballBabyRanking(Model model, HttpServletResponse response) {
List<BabyRanking> ranks = new ArrayList<BabyRanking>();
// FIXME 这是测试数据,向君仔要数据
for (int i = 1; i <= 20; i++) {
BabyRanking rank = new BabyRanking();
rank.setBust(90);
rank.setWaist(90);
rank.setHip(90);
rank.setImageUrl("/resources/nantianmen/images/" + i + ".jpg");
rank.setName("fb" + i);
rank.setVotes(23 + i);
rank.setOrder(i);
ranks.add(rank);
}
model.addAttribute("ranks", ranks);
List<BabyRanking> lastWeekRanks = new ArrayList<BabyRanking>();
for (int i = 1; i <= 6; i++) {
BabyRanking rank = new BabyRanking();
rank.setBust(88);
rank.setWaist(88);
rank.setHip(88);
rank.setImageUrl("/resources/nantianmen/images/" + i + ".jpg");
rank.setName("fb" + i);
rank.setVotes(32 + i);
rank.setOrder(i);
lastWeekRanks.add(rank);
}
model.addAttribute("lastWeekRanks", lastWeekRanks);
String cacheControlValue = CacheControlHeader.newBuilder().setCacheType(CacheType.PUBLIC)
.setMaxAge(300).setsMaxAge(600).build().stringValue();
// String cacheControlValue =
// CacheControlHeader.newBuilder().setNoCache(true)
// .setMustRevalidate(true).build().stringValue();
if (StringUtils.isNotBlank(cacheControlValue)) {
response.addHeader("Cache-Control", cacheControlValue);
}
// TODO 需要前面几期的排名
return "football-baby";
}
@RequestMapping(value = "/baby/news", method = RequestMethod.GET)
public String footballBabyNews(Model model) {
List<BabyNews> news = new ArrayList<BabyNews>();
for (int i = 1; i <= 5; i++) {
BabyNews oneNews = new BabyNews();
oneNews.setContent("");
oneNews.setDate(new Date());
oneNews.setImageUrl("/resources/nantianmen/images/apple-touch-icon-57x57-precomposed.png");
oneNews.setSource("Yahoo");
oneNews.setTitle("Title");
oneNews.setUrl("www.baidu.com");
news.add(oneNews);
}
model.addAttribute("news", news);
return "football-news";
}
}
Created
April 25, 2014 11:56
-
-
Save arganzheng/11287077 to your computer and use it in GitHub Desktop.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment