利用Spring AOP 更新memcached 緩存策略的實現
對于網上關于memcached緩存更新策略 數不勝數,但是沒有一遍完整的,看起來都很費勁,由于項目中用到的memcache,自然就想到了memcache緩存更新策略的實現。
你可以把你更新緩存的代碼嵌套你的代碼中,但是這樣很不好,混換了你service的代碼,要是以后再換別的緩存產品,那么你還要每個類去找,去修改很是麻煩。由于之前是這樣寫的,很是痛苦,所以這次要用spring aop來實現。
在做本次試驗之前 ,首先要準備好memcache,具體安裝步驟請參考:http://blog.csdn.net/ajun_studio/article/details/6745341
了解memcache,請參考:對memcached使用的總結和使用場景
下面說以下具體更新策略的實現思路:
首先我們會定義兩個注解類,來說明是插入(Cache)緩存還是刪除(Flush)緩存,這兩個類可以方法上,來對你service需要進行緩存方法操作進行注解標記,注解類內用key的前綴,和緩存的有效時間,接著spring aop 攔截service層含有這個兩個注解的方法,獲得key的前綴+方法明+參數組裝成key值,存入到一張臨時表內,如果是Cache注解的話,先判斷,緩沖中有沒,有從緩存中取得,沒有從數據庫中查詢,然后存緩存,返回結果。如果是Flush注解,說明是刪除緩存,那么首先獲得注解中key值的前綴,查詢庫中所以以這個為前綴的key,查詢出來 ,刪除數據庫中的數據,最后在刪除緩存中所有的符合這些key的緩存,來達到更新緩存。另外這張臨時表,可以做個定時任務日終的時候 ,刪除一些無用的數據。
具體代碼:
Cache注解
package com.woaika.loan.commons.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 用于在查詢的時候 ,放置緩存信息
* @author ajun
* @email zhaojun2066@gmail.com
* @blog http://blog.csdn.net/ajun_studio
* 2012-2-27 上午10:42:06
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Cache {
String prefix();//key的前綴,如咨詢:zx
long expiration() default 1000*60*60*2;//緩存有效期 1000*60*60*2==2小時過期
}Flush注解
package com.woaika.loan.commons.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 用于刪除緩存
* @author ajun
* @email zhaojun2066@gmail.com
* @blog http://blog.csdn.net/ajun_studio
* 2012-2-27 上午10:53:03
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Flush {
String prefix();//key的前綴,如咨詢:zx
}臨時表用于存儲key值
CREATE TABLE `cache_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`prefix` varchar(50) DEFAULT NULL COMMENT 'key的前綴',
`cache_key` varchar(300) DEFAULT NULL COMMENT 'key值',
`add_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 memcache客戶端代碼:需要java_memcached-release_2.6.2.jar
package com.woaika.loan.commons.cache;
import java.util.Date;
import com.danga.MemCached.*;
import com.woaika.loan.commons.constants.CacheConstant;
public class Memcache {
static MemCachedClient memCachedClient=null;
static{
String[] servers = { CacheConstant.SERVIERS};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
// 設置初始連接數、最小和最大連接數以及最大處理時間
/* pool.setInitConn(5);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaxIdle(1000 * 60 * 60 * 6); */
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30); // 設置主線程的睡眠時間
// 設置TCP的參數,連接超時等
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
memCachedClient = new MemCachedClient();
memCachedClient.setPrimitiveAsString(true);//錕斤拷錕叫伙拷
}
public static Object get(String key)
{
return memCachedClient.get(key);
}
// public static Map<String,Object> gets(String[] keys)
// {
// return memCachedClient.getMulti(keys);
// }
public static boolean set(String key,Object o)
{
return memCachedClient.set(key, o);
}
public static boolean set(String key,Object o,Date ExpireTime)
{
return memCachedClient.set(key, o, ExpireTime);
}
public static boolean exists(String key)
{
return memCachedClient.keyExists(key);
}
public static boolean delete(String key)
{
return memCachedClient.delete(key);
}
}spring AOP代碼 基于注解
package com.woaika.loan.front.common.aop;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import com.woaika.loan.commons.annotation.Cache;
import com.woaika.loan.commons.annotation.Flush;
import com.woaika.loan.commons.cache.Memcache;
import com.woaika.loan.po.CacheLog;
import com.woaika.loan.service.log.ICacheLogService;
/**
* 攔截緩存
* @author ajun
* @email zhaojun2066@gmail.com
* @blog http://blog.csdn.net/ajun_studio
* 2012-3-12 上午10:51:58
*/
@Component
@Aspect
public class CacheAop {
private ICacheLogService cacheLogService;
@Resource(name="cacheLogService")
public void setCacheLogService(ICacheLogService cacheLogService) {
this.cacheLogService = cacheLogService;
}
//定義切面
@Pointcut("execution(* com.woaika.loan.service..*.*(..))")
public void cachedPointcut() {
}
@Around("cachedPointcut()")
public Object doAround(ProceedingJoinPoint call){
Object result = null;
Method[] methods = call.getTarget().getClass().getDeclaredMethods();
Signature signature = call.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
for(Method m:methods){//循環方法,找匹配的方法進行執行
if(m.getName().equals(method.getName())){
if(m.isAnnotationPresent(Cache.class)){
Cache cache = m.getAnnotation(Cache.class);
if(cache!=null){
String tempKey = this.getKey(method, call.getArgs());
String prefix = cache.prefix();
String key = prefix+"_"+tempKey;
result =Memcache.get(key);
if(null == result){
try {
result = call.proceed();
long expiration = cache.expiration();//1000*60*60*2==2小時過期
Date d=new Date();
d=new Date(d.getTime()+expiration);
Memcache.set(key, result, d);
//將key存入數據庫
CacheLog log = new CacheLog();
log.setPrefix(prefix);
log.setCacheKey(key);
this.cacheLogService.add(log);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
} else if(method.isAnnotationPresent(Flush.class)){
Flush flush = method.getAnnotation(Flush.class);
if(flush!=null){
String prefix = flush.prefix();
List<CacheLog> logs= cacheLogService.findListByPrefix(prefix);
if(logs!=null && !logs.isEmpty()){
//刪除數據庫
int rows = cacheLogService.deleteByPrefix(prefix);
if(rows>0){
for(CacheLog log :logs){
if(log!=null){
String key = log.getCacheKey();
Memcache.delete(key);//刪除緩存
}
}
}
}
}
}else{
try {
result = call.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
break;
}
}
return result;
}
/**
* 組裝key值
* @param method
* @param args
* @return
*/
private String getKey(Method method, Object [] args){
StringBuffer sb = new StringBuffer();
String methodName = method.getName();
sb.append(methodName);
if(args!=null && args.length>0){
for(Object arg:args){
sb.append(arg);
}
}
return sb.toString();
}
}service層方法添加注解,但是里面代碼沒有添加任何memcache客戶端的代碼,達到降低耦合性:
@Cache(prefix=CacheConstant.ANLI,expiration=1000*60*60*10)
@Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly=true)
public QueryResult findAll(Integer firstIndex, Integer pageSize) {
Map condition = new HashMap();
return loanCaseDao.findByCondition(condition, LoanCase.class, firstIndex, pageSize);
}以上是主要代碼的實現思路,希望對同志們的有所幫助,關于spring aop 自行google下就知道了
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!