jfinal内置CacheInterceptor
- 依赖于EhCachePlugin,是基于ehcache的。
- CacheInterceptor 可以将 action 所需数据全部缓存起来,下次请求到来时如果 cache 存在则直接使用数据并 render,而不会去调用 action。此用法可使 action 完全不受 cache相关代码所 污染,即插即用,以下是示例代码:
@Before(CacheInterceptor.class)
public void list() {
List<Blog> blogList = Blog.dao.find("select * from blog");
User user = User.dao.findById(getParaToInt());
setAttr("blogList", blogList);
setAttr("user", user);
render("blog.html");
}
- EvictInterceptor 可以根据 CacheName 注解自动清除缓存。以下是示例代码: 上例中的用法将清除 cacheName 为 blogList 的缓存数据,与其配合的 CacheInterceptor 会 自动更新 cacheName 为 blogList 的缓存数据。
扩展CacheInterceptor支持Redis缓存
public class RedisCacheInterceptor implements Interceptor {
private static final String renderKey = "_renderKey";
private static final String prefixKey = "intercept_";
private static ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<String, ReentrantLock>();
private ReentrantLock getLock(String key) {
ReentrantLock lock = lockMap.get(key);
if (lock != null)
return lock;
lock = new ReentrantLock();
ReentrantLock previousLock = lockMap.putIfAbsent(key, lock);
return previousLock == null ? lock : previousLock;
}
public void intercept(Invocation inv) {
Controller controller = inv.getController();
String cacheName = buildCacheName(inv, controller);
String cacheKey = buildCacheKey(inv, controller);
Map<String, Object> cacheData = Redis.use().hget(cacheName, cacheKey);
if (cacheData == null) {
Lock lock = getLock(cacheName);
lock.lock();
try {
cacheData = Redis.use().hget(cacheName, cacheKey);
if (cacheData == null) {
inv.invoke();
cacheAction(cacheName, cacheKey, controller);
return;
}
} finally {
lock.unlock();
}
}
useCacheDataAndRender(cacheData, controller);
}
private String buildCacheName(Invocation inv, Controller controller) {
CacheName cacheName = inv.getMethod().getAnnotation(CacheName.class);
if (cacheName != null)
return prefixKey + cacheName.value();
cacheName = controller.getClass().getAnnotation(CacheName.class);
return (cacheName != null) ? prefixKey + cacheName.value() : prefixKey + inv.getActionKey();
}
private String buildCacheKey(Invocation inv, Controller controller) {
StringBuilder sb = new StringBuilder(inv.getActionKey());
String urlPara = controller.getPara();
if (urlPara != null)
sb.append("/").append(urlPara);
String queryString = controller.getRequest().getQueryString();
if (queryString != null)
sb.append("?").append(queryString);
return sb.toString();
}
private void cacheAction(String cacheName, String cacheKey, Controller controller) {
HttpServletRequest request = controller.getRequest();
Map<String, Object> cacheData = new HashMap<String, Object>();
for (Enumeration<String> names = request.getAttributeNames(); names.hasMoreElements(); ) {
String name = names.nextElement();
cacheData.put(name, request.getAttribute(name));
}
Render render = controller.getRender();
if (render != null) {
cacheData.put(renderKey, createRenderInfo(render)); // cache RenderInfo
}
Redis.use().hset(cacheName, cacheKey, cacheData);
}
private RenderInfo createRenderInfo(Render render) {
return new RenderInfo(render);
}
private void useCacheDataAndRender(Map<String, Object> cacheData, Controller controller) {
HttpServletRequest request = controller.getRequest();
Set<Map.Entry<String, Object>> set = cacheData.entrySet();
for (Map.Entry<String, Object> entry : set) {
request.setAttribute(entry.getKey(), entry.getValue());
}
request.removeAttribute(renderKey);
RenderInfo renderInfo = (RenderInfo) cacheData.get(renderKey);
if (renderInfo != null) {
controller.render(renderInfo.createRender());
}
}
}
扩展RedisEvictInterceptor,清除缓存
/**
* Created by jie on 2017/4/5.
* 根据CacheName清除
* 必须配合 @CacheName 使用
*/
public class RedisEvictInterceptor implements Interceptor {
private static final String prefixKey = "intercept_";
final public void intercept(Invocation inv) {
inv.invoke();
Redis.use().del(buildCacheName(inv));
}
private String buildCacheName(Invocation inv) {
CacheName cacheName = inv.getMethod().getAnnotation(CacheName.class);
if (cacheName != null)
return prefixKey + cacheName.value();
cacheName = inv.getController().getClass().getAnnotation(CacheName.class);
if (cacheName == null)
throw new RuntimeException("EvictInterceptor need CacheName annotation in controller.");
return prefixKey + cacheName.value();
}
}
使用
@Before(RedisCacheInterceptor.class)
public void test(){
render("test/test.html");
}
@Before(RedisCacheInterceptor.class)
@CacheName("test1")
public void test1(){
render("test/test.html");
}
Redis缓存结果