缓存的实现最简单的模式,我称作为get set invalid 法,这也是缓存中最常用的一种模式,以前实现缓存(特别是后加)一般都会写一大堆诸如:
Object target = cache.get(KEY)
if(target!=null){
//do something
}else{
//do set
}
这个其实很不好看,代码很不优雅.当然你可以使用一些设计模式去解决比如说圆盘套圆盘法---函数模板...但实现起来并那么容易有时候会导致代码可读性下降,而且缓存的key可能一样不灵活.
现在好了,我们可以尝试用SPRING的AOP来做:
<?xml version="1.0" encoding="gbk"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd" default-autowire="byName"> <bean id="AopManagerForCache" class="org.edwardpro.common.aop.CacheAOPManagerImpl"> <property name="expTime"> <value>900</value> </property> </bean> <aop:config> <aop:aspect id="dataAOP" ref="AopManagerForCache"> <aop:around pointcut="execution(* org.edwardpro.aop.biz.core.manager.*Manager.*(..))" method="checkJointPoint" /> </aop:aspect> </aop:config> </beans>
这样在指定包的地方都会被拦截,方法被拦截了干什么还是一个问题,这里我使用了注入,一共有两类注入,一种叫CheckCache一种叫FlushCache.Flush是用来delete缓存的 checkcache是用来检查缓存的,典型的get时候用check而update时候用flush.由于缓存key是来自于参数的,所以这个注解我定义了三个参数:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheCheck {
/**
* 缓存的前缀,就是缓存那些参数前面的修饰符号: MSP_SHOP_SHOPINFO
*
* 这个字段支持表达式了,表达式的含义是最后输出的前缀是什么并会影响整个键值生成系统
* 表达式的样例:
* expr:{0 equa}
* @return
*/
public String cachePrefix();
/**
* 活动参数的表达式,这个需要用拦截器获得的,这里只是把参数列举出来,最终的形式: _{value}_
*
* 如果是复杂对象,使用 0.aaa 1 2.ccc的形式来写
*
*
* @return
*/
public String[] paramList();
/**
* New Add
*
* 有些缓存刷新需要返回值,比如分页缓存的刷新 方便起见格式用json写:
*
* {'isRange':'0','range-start':'1','range-end':'10','range-step':'1'}
*
* @return
*/
public String range();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FlushCache {
/**
* 缓存的前缀,就是缓存那些参数前面的修饰符号: MSP_SHOP_SHOPINFO
*
* @return
*/
public String[] cachePrefix();
/**
* 活动参数的表达式,这个需要用拦截器获得的,这里只是把参数列举出来,最终的形式: _{value}_
*
* 如果是复杂对象,使用 0.aaa 1 2.ccc的形式来写
*
*
* @return
*/
public String[] paramList();
/**
* New Add
*
* 有些缓存刷新需要返回值,比如分页缓存的刷新 方便起见格式用json写:
*
* {'isRange':'0','range-start':'1','range-end':'10','range-step':'1'}
*
* @return
*/
public String[] range();
}
参数的表达是这样的 (0.xx) xx一定是要set get方法的属性值,因为我的实现依赖于beanutil. 0,1,2 是参数的位置,因为在aop后参数名没有了,虽然也有方法可以得到,但是那个很麻烦,省事期间我还是用0,1,2这种表示是哪个参数,下面看下解析参数用的类:
/**
* aop的工具之一用来通过参数表解析式得到参数信息
*
* @param paramStr
* @param obj
* @return paramStr 最简单形式是 0 0.aa
*/
public static List<Object> getValueByStr(String paramStr, Object[] obj) {
List<Object> ret = new ArrayList<Object>();
String[] args = paramStr.split("\\.");
if (args != null && args.length >= 1) {
// 原始对象
Object valueOrg = obj[Integer.parseInt(args[0])];
if (valueOrg instanceof Collection) {
for (Object childObj : (Collection) valueOrg) {
if (args.length == 1) {
ret.add(childObj);
continue;
}
for (int i = 1; i < args.length; i++) {
PropertyDescriptor pd = BeanUtils
.getPropertyDescriptor(childObj.getClass(),
args[i]);
try {
// 增加容错代码
if (pd != null) {
childObj = pd.getReadMethod().invoke(childObj);
}
if (i == (args.length - 1)) {
ret.add(childObj);
}
} catch (IllegalArgumentException e) {
continue;
} catch (IllegalAccessException e) {
continue;
} catch (InvocationTargetException e) {
continue;
}
}
}
} else {
if (args.length == 1) {
ret.add(valueOrg);
}
for (int i = 1; i < args.length; i++) {
PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(
valueOrg.getClass(), args[i]);
try {
// 增加容错代码
if (pd != null) {
valueOrg = pd.getReadMethod().invoke(valueOrg);
}
if (i == (args.length - 1)) {
ret.add(valueOrg);
}
} catch (IllegalArgumentException e) {
continue;
} catch (IllegalAccessException e) {
continue;
} catch (InvocationTargetException e) {
continue;
}
}
}
// return valueOrg;
}
return ret;
代码比较粗俗随便看看就是了~_~
然后差不多把这些组合起来:
/*
* (non-Javadoc)
*
* @seecom.taobao.common.aop.CacheAOPManager#checkCache(org.aspectj.lang.
* ProceedingJoinPoint, com.taobao.common.annotation.cache.CacheCheck)
*/
@Override
public Object checkCache(ProceedingJoinPoint jointPoint,
CacheCheck cacheCheck) {
String key = this.getCheckKey(cacheCheck, jointPoint.getArgs());
Result<DataEntry> ret = this.getTairManager().get(this.getNameSpace(),
key);
logger.info(">>checkValue>>" + ret.getRc().getMessage());
if (ret != null && ret.getRc().getCode() == 0) {
Object obj = ret.getValue().getValue();
return obj;
} else {
try {
// 没有得到缓存所以会执行对象
logger.info(">>checkValue>>No Cache");
Object obj = jointPoint.proceed();
ResultCode retCode = this.getTairManager().put(
this.getNameSpace().intValue(), key,
(Serializable) obj, 0, this.getExpTime());
logger.info("set cache result>>" + retCode.getCode() + "|"
+ retCode.getMessage());
return obj;
} catch (Throwable e) {
logger.error(e);
}
}
return null;
}
/*
* (non-Javadoc)
*
* @seecom.taobao.common.aop.CacheAOPManager#flushCache(org.aspectj.lang.
* ProceedingJoinPoint, com.taobao.common.annotation.cache.FlushCache)
*/
@Override
public Object flushCache(ProceedingJoinPoint jointPoint,
FlushCache flushCache) {
List<Object> keyList = this.getFlushKey(flushCache, jointPoint
.getArgs());
ResultCode ret = this.tairManager.mdelete(this.getNameSpace()
.intValue(), keyList);
logger.info(">>flush cache result>>" + keyList + ">>" + ret.getCode()
+ ">>" + ret.getMessage());
try {
return jointPoint.proceed();
} catch (Throwable e) {
logger.error(e);
}
return null;
}
/**
* 通过参数得到前缀
*
* @param flushCache
* @param args
* @return
*/
public List<Object> getFlushKey(FlushCache flushCache, Object[] args) {
List<Object> ret = new ArrayList<Object>();
if (flushCache != null) {
for (int i = 0; i < flushCache.cachePrefix().length; i++) {
String prefix = flushCache.cachePrefix()[i];
List<String> paramList = this.genParamList(flushCache, i, args,
prefix);
List<String> rangeList = this.genRange(flushCache, i);
for (String paramItem : paramList) {
if (rangeList.size() > 0) {
for (String rangeItem : rangeList) {
StringBuilder sb = new StringBuilder();
sb.append(paramItem);
sb.append(rangeItem);
ret.add(sb.toString());
}
} else {
// StringBuilder sb = new StringBuilder();
// sb.append(paramItem);
ret.add(paramItem);
}
}
}
this.removeDuplicated(ret);
}
return ret;
}
private List<String> genParamList(FlushCache flushCache, int index,
Object[] args, String prefix) {
List<String> ret = new ArrayList<String>();
List<String> paramList;
if (index <= (flushCache.paramList().length - 1)) {
// 对应的关系
paramList = this.getPrefix(flushCache.paramList()[index]);
} else {
paramList = this.getPrefix(flushCache.paramList()[0]);
}
for (String argStr : paramList) {
List<Object> argsValue = valueProcessManager.getValue(argStr, args);
if (ret.size() > 0) {
List<String> tempRet = new ArrayList<String>();
for (String tempItem : ret) {
for (Object tempObj : argsValue) {
StringBuilder sbTemp = new StringBuilder();
sbTemp.append(tempItem);
sbTemp.append(KEY_SPILIT);
sbTemp.append(tempObj);
tempRet.add(sbTemp.toString());
}
}
ret.clear();
ret.addAll(tempRet);
} else {
for (Object item : argsValue) {
ret.add(prefix + KEY_SPILIT + item.toString());
}
}
}
if (ret.size() == 0) {
ret.add(prefix + KEY_SPILIT);
}
return ret;
}
private List<String> genRange(FlushCache flushCache, int index) {
List<String> ret = new ArrayList<String>();
if (null != flushCache.range()) {
String range;
if (index <= (flushCache.range().length - 1)) {
// 对应的关系
range = flushCache.range()[index];
} else {
range = flushCache.range()[0];
}
Map<String, Integer> map = JsonUtils.mapFromJson(range,
Integer.class);
KeyRanger keyRanger = KeyRanger.getInstance(map);
if (keyRanger.getIsRange() > 0) {
for (int j = keyRanger.getRangeStart(); j <= keyRanger
.getRangeEnd(); j = j + keyRanger.getRangeStep()) {
StringBuilder sb = new StringBuilder();
sb.append(KEY_SPILIT);
sb.append(j);
ret.add(sb.toString());
}
}
}
return ret;
}
// 去除重复KEY
private void removeDuplicated(List<Object> input) {
List<Object> tempList = new ArrayList<Object>(input);
HashSet<Object> tempSet = new HashSet<Object>(tempList);
input.clear();
for (Object obj : tempSet) {
input.add(obj);
}
}
public String getCheckKey(CacheCheck cacheCheck, Object[] args) {
StringBuffer ret = new StringBuffer();
ret.append(cacheCheck.cachePrefix());
for (String argStr : cacheCheck.paramList()) {
List<Object> argsValue = valueProcessManager.getValue(argStr, args);
if (argsValue != null) {
for (Object oChild : argsValue) {
if (oChild != null) {
ret.append(KEY_SPILIT);
ret.append(oChild);
}
}
}
}
logger.info(ret.toString());
return ret.toString();
}
private List<String> getPrefix(String keys) {
List<String> ret = new ArrayList<String>();
if (null != keys) {
String[] arr = keys.split(",");
if (null != arr) {
for (String s : arr) {
if (!TBStringUtil.isBlank(s)) {
ret.add(s);
}
}
}
}
return ret;
}
变成贴代码了....天太热不想多打字,呵呵,大家凑合看,基本上看下spring配置就知道大概的意图了,试用下来对于一般的前后台应用足够了,但是要注意,拦截的方法一定是要业务层的,另外前后台的get方法最好分开,否则缓存一旦配置有问题,就会导致后台更新不了的故障,这个要注意...
over,其实只是想提供一种思路至于实现就贻笑大方了...