基于CacheInterceptor的 缓存管理工具类

赖芳升 发布于 2013/02/02 17:07
阅读 1K+
收藏 6

今天研究Jfinal的缓存的部分的时候,使用了CacheInterceptor,

这个类主要实现了: 如果不存在跟action相关的缓存,则在数据库查询,存在则跳过action执行的功能。

但是存在一个缓存更新的问题, 就是增、删、改数据库内容之后,被CacheIntercepto拦截的action 存放的缓存不能及时更新。虽然可手动使用 CacheKit.remove(name,key) 方法 移除过时的缓存,但是不够方便,所以自己动手写了一个工具类来管理。

 使用了一个CacheManager 拦截器和Annotation 注解

    CacheManager 实现了 类似于 demo中 ,检测到若果执行的是未被Annotation 指定(index 默认被指定)的action 则清除action相关的缓存的功能.

    CacheMethod  Annotation类 ,用于标注类似于index search 至于查询相关的action,不使用注解和不指定注解值时默认为index,如果使用了则必须包含index

    CacheInterceptor 源码的cache和key命名规则是这样的

    cache: 通过注解指定的 ,则使用注解,没有的则使用action key (如:/bolg/update,但index这个action则是/blog)

    key:使用 actionkey 加上相关请求参数


public @interface CacheMethod {
	String[] methodName() default "index";
}


 public class BlogInterceptor implements Interceptor {
	private static Logger logger= Logger.getLogger(BlogInterceptor.class);
	
	public void intercept(ActionInvocation ai) {
		String[] methods = getCacheMethod(ai);
		String method = ai.getMethodName();
		for (String temp : methods) {
			
			if (!temp.equals(method)) {
				Controller ctl = ai.getController();
				String cacheName=buildCacheName(ai, ctl);
				removeCache(cacheName, temp,ai.getControllerKey());
			}
		}
		ai.invoke();
	}
	
	private void removeCache(String cacheName,String method ,String controllerKey){
		@SuppressWarnings("unchecked")
		List<String> keyList=CacheKit.getKeys(cacheName);
		for(String key:keyList){
			//method index的情况
			if(key.equals(controllerKey)&&method.equals("index")){
				CacheKit.remove(cacheName, key);
				logger.info("remove cache [name:"+cacheName+",key:"+key+"]");
			}
			//method 一般情况
			if(key.contains(controllerKey+"/"+method)){
				CacheKit.remove(cacheName, key);
				logger.info("remove cache [name:"+cacheName+",key:"+key+"]");
			}
		}
		
	}

	/**
	 * Get methodsName from Annotation in Controller,if has not annotation return "index"
	 * 
	 * @param ai
	 * @return methodsName
	 */
	private String[] getCacheMethod(ActionInvocation ai) {
		Controller ctl = ai.getController();
		CacheMethod cacheMethod = ctl.getClass().getAnnotation(
				CacheMethod.class);
		if(cacheMethod==null){
			return new String[]{"index"};
		}
		String[] methods = cacheMethod.methodName();
		
		return methods;
	}

	private String buildCacheName(ActionInvocation ai, Controller controller) {
		CacheName cacheName = ai.getMethod().getAnnotation(CacheName.class);
		if (cacheName != null)
			return cacheName.value();
		cacheName = controller.getClass().getAnnotation(CacheName.class);
		if (cacheName != null)
			return cacheName.value();
		return ai.getActionKey();
	}

	@SuppressWarnings("unused")
	private String buildCacheKey(ActionInvocation ai, Controller controller) {
		StringBuilder sb = new StringBuilder(ai.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();
	}
}



@Before(BlogInterceptor.class)
@CacheMethod(methodName={"index","add","edit"})
@CacheName(value = "BlogCache")
public class BlogController extends Controller {
	
	@Before(CacheInterceptor.class)
	public void index() {
		setAttr("blogPage", Blog.dao.paginate(getParaToInt(0, 1), 10,
				"select *", "from blog order by id desc"));
		render("blog.html");
	}

	public void add() {

	}

	@Before(BlogValidator.class)
	public void save() {
		getModel(Blog.class).save();
		redirect("/blog");
	}

	public void edit() {
		setAttr("blog", Blog.dao.findById(getParaToInt()));
	}

	@Before(BlogValidator.class)
	public void update() {
		getModel(Blog.class).update();
		redirect("/blog");
	}

	public void delete() {
		Blog.dao.deleteById(getParaToInt());
		redirect("/blog");
	}
}


加载中
0
JFinal
JFinal
思路很好, JFinal 下一版考虑提供一个 CacheEvict 来实现类似的功能,谢谢分享 
0
okk
okk
好方法!
0
Rezeroer
Rezeroer

引用来自“JFinal”的答案

思路很好, JFinal 下一版考虑提供一个 CacheEvict 来实现类似的功能,谢谢分享 

在Action进行缓存的时候 是否会有问题呢? 如果从页面以Post的方式传来数据 但是它请求的URL都是一样的那 缓存会不会直接Render之前请求的数据呢?

0
JFinal
JFinal

引用来自“第七页”的答案

引用来自“JFinal”的答案

思路很好, JFinal 下一版考虑提供一个 CacheEvict 来实现类似的功能,谢谢分享 

在Action进行缓存的时候 是否会有问题呢? 如果从页面以Post的方式传来数据 但是它请求的URL都是一样的那 缓存会不会直接Render之前请求的数据呢?

Post 请求一般是对数据进行更新,所以此时不建议使用Cache
0
Rezeroer
Rezeroer

引用来自“JFinal”的答案

引用来自“第七页”的答案

引用来自“JFinal”的答案

思路很好, JFinal 下一版考虑提供一个 CacheEvict 来实现类似的功能,谢谢分享 

在Action进行缓存的时候 是否会有问题呢? 如果从页面以Post的方式传来数据 但是它请求的URL都是一样的那 缓存会不会直接Render之前请求的数据呢?

Post 请求一般是对数据进行更新,所以此时不建议使用Cache
不知道  在拦截器里面能否判断出来请求方式是Get 或者 Post的   既然不建议使用的话  那以Post方式请求过来的可以不加入缓存么? 不然的话,,,,,,,
0
绝望的八皮
绝望的八皮
以前做过一个参考spring3.1的抽象缓存做的缓存工具,做在service层的。有更新策略,可以用 CacheEvict配置更新的ognl表达式。楼主可以参考参考这种思想,提供 CacheEvict配置表达式来觉得清空对应key的缓存数据
赖芳升
赖芳升
现在回过头来看了一下代码,发现很多逻辑性错误,在对比了spring的做法之后,更觉得自己考虑的不周全。谢谢提醒
0
dandyIder
dandyIder

make

这代码得从下往上看

返回顶部
顶部