上一篇博客针对Key值生成策略和Redis数据源读取的问题,想了一些解决的思路,停滞了一段时间后,最近实现好了。下面就根据实现,再结合解决思路说明一下。本篇博客先说KEY值生成策略的问题。
前提
我们的系统是Spring mvc + EJB实现的。因为分布式调用的问题,为了避免脏数据我们的数据缓存需要加在Service层。这个之前没有交代过,通过下面这个图,解释一下:
图中①和②分别表明了两个加数据缓存的位置,另外如图,远程调用是发生在EJB容器之间的。另外,查询方法的结果加入缓存后还需要清除在增、删、改这些操作的时候清除缓存,以保证不出现脏数据。如果是在模块1的①的位置加入数据缓存,如果缓存存在时就不会再往后调用了。而问题是如果这个方法的数据涉及到了其模块2的数据,模块2的数据变更是无法通知到模块1的IOC容器的。那么脏数据就无法避免了。
由上可知,数据缓存需要加在②位置,并且是没有发生远程调用的方法上。因为同一个类上的对数据操作是可知的。这样,加缓存和清缓存的操作就可知了。
这个前提决定了缓存的操作可以精确到类,也就决定了KEY值的生成策略(请看上篇博客:Redis+EJB实现缓存(三)),加缓存的KEY值可以以类名+方法名+参数,删除缓存则以类为单位删除。
加缓存的拦截器方法实现
public Object cache(InvocationContext ctx) throws Exception{
System.out.println("进入拦截器");
if(cachepro.getFlag() == false){
InputStream in = (ctx.getTarget().getClass().getResourceAsStream("/config/cache.xml"));
byte[] byt = new byte[in.available()];
in.read(byt);
cache.init(byt);
}
//取得类名
String className = ctx.getTarget().getClass().getSimpleName();
//取得方法名
String methodName = ctx.getMethod().getName();
//取得参数
Object[] args = ctx.getParameters();
String arg = "";
for(int i=0;i<args.length;i++)
{
arg = arg + args[i].toString();
}
//主键值,由系统名称+类名,删除时用
String mainKey = cachepro.getPrefixion() + className;
//次级键值由主键值再加上方法名和参数
String key = mainKey + methodName + arg;
//次级键值存在则返回结果
if(cache.ishave(key)){
return cache.get(key);
}else{
//次级键值不存在则将次级键值存入主键值下
Object result = ctx.proceed();
cache.set(mainKey, key);
cache.set(key,result);
return null;
}
}
删除缓存拦截器方法实现
public Object cache(InvocationContext ctx) throws Exception{
if(cachepro.getFlag() == false){
InputStream in = (ctx.getTarget().getClass().getResourceAsStream("/config/cache.xml"));
byte[] byt = new byte[in.available()];
in.read(byt);
cache.init(byt);
}
//取得类名
String className = ctx.getTarget().getClass().getSimpleName();
//系统名+类名生成主键值,对应添加时的
String mainKey = cachepro.getPrefixion() + className;
//删除时,取得主键值下的次级键值,删除缓存
cache.del(mainKey);
return null;
}
这样,键值的生成策略就算是基本完成了。但是后面思考了一下,当参数类型是Object时还有问题。
问题描述
Object的toString方法,是getClass().getName()+ '@' + Integer.toHexString(hashCode())这个hashCode只是标明了类的在hashTable中的位置。换句话说,两个一样的类由于两次生成的类,可能不存在同一个位置下。那么Object参数作为键值时就无法精确定位同一个键值,那么这样在清除缓存时就无法清除了。
解决思路
作为参数的Object都是实体,因此我们需要重写toString方法,使得类中的字段一样时返回的String是一样的。这样就解决了,两个实体属性一样的类作为参数时,生成的键值是一样的。问题就解决了。
小结,新的问题,明天就实现,应该是可行。下篇博客我们再说,Redis数据源的解决实现。这篇博客就到这里了。上面的代码主要看注释的那部分。