package org.langke.common.cache;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.langke.common.Config;
import org.langke.common.CostTime;
import org.langke.common.ExecutorFactory;
import org.langke.util.logging.ESLogger;
import org.langke.util.logging.Loggers;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
*
* @author langke
* @created 2012-8-28
* 如果参数是Map 取Map中的memKey作为key ,如果Map中没有memKey则取id作为key
* 如果参数是基础类型java.lang.* 、数组、原始类型或基础类型: 所有参数相加作为key
* 如果参数是实体类,取实体类中的PK,如果没有取实体类中id作为key
* 缓存默认10分钟刷新,可以config.properties 设置 memcachedExpiryTime
*
* 在需要缓存的方法加注解:@MemCaching
* 在需要清除缓存的方法加注解:@MemFlush
*
* 配置文件加:
isUsedMemcached=true
tt.cache.server = newmall.mem.tg.local:11211
memcachedPre = api_review_
#memcached expiry time ms
memcachedExpiryTime=600000
* @version 0.1 入缓存走异步
* @lastmodify 2012-09-28
*/
@Aspect
@Component
public class CacheManager {
private static ESLogger log = Loggers.getLogger(CacheManager.class);
MemcachedUtil memcachedClient = MemcachedUtil.getInstance();
@SuppressWarnings("unused")
@Pointcut("@annotation (com.qjmall.common.cache.MemCaching) || @annotation (com.qjmall.common.cache.MemFlush)")
private void anyMethod() {}
@Around(value="anyMethod()")
public Object doAccessCheck(ProceedingJoinPoint pjp) throws Throwable{
Object target=pjp.getTarget();
String methodName=pjp.getSignature().getName();
Object[] args=pjp.getArgs();
Method method=getMethod(target,methodName,args);
if(method == null){//找不到方法
log.warn("mot match method:{} {}", methodName,args);
return pjp.proceed(args);
}
String memKey = null;
boolean needCache=method.isAnnotationPresent(MemCaching.class);//有加些注解的加缓存
boolean flushCache=method.isAnnotationPresent(MemFlush.class); //刷新缓存
boolean isUsedMemcached = Config.get().getBoolean("isUsedMemcached", true);//是否启用缓存
if((!needCache && !flushCache) ||!isUsedMemcached){//不需要走缓存
return pjp.proceed(args);
}
CostTime cost = new CostTime();
cost.start();
for (Object obj :args){
if(obj instanceof Map){//如果参数是Map,取Map中的id作为key
if(((Map<?, ?>)obj).containsKey("memKey")){//取Map中的memKey作为key
memKey = (String) ((Map<?, ?>)obj).get("memKey");
}
if(memKey == null && ((Map<?, ?>)obj).containsKey("id")){//如果没有设置,取Map中的id作为key
memKey = (String) ((Map<?, ?>)obj).get("id");
}
break;
}
}
if(memKey==null){//参数不是map
Class<?> entryclass = null;//=args[0].getClass();
for(Object arg:args){
if(arg == null)
continue;
entryclass = arg.getClass();//找到一个非空的参数
break;
}
if(entryclass!=null){
if(entryclass.isArray() || entryclass.isPrimitive() || entryclass.toString().indexOf("java.lang") > 0 || ClassUtils.isAssignable(entryclass, Map.class)){//数组、原始类型或基础类型
memKey = "";
for(Object obj : args){
if(obj == null)
continue;
else if(obj.getClass().isArray())
obj = ArrayUtils.toString(obj);
memKey += String.valueOf(obj);//所有参数相加做为memKey
}
}else if(entryclass.toString().indexOf("java.util") > 0){
log.warn("{}不支持java.util类型除Map以外的参数",methodName);
}else{//实体类
//如果参数是实体类,取实体类中的PK
if(entryclass instanceof Serializable){
//取实体类中带PK注解的属性作为memkey
Method[] Entrymethods = entryclass.getDeclaredMethods();
for(Method mhd : Entrymethods){
if(mhd.isAnnotationPresent(PK.class)){
memKey = (String) mhd.invoke(args[0]);
break;
}
}
if(memKey == null){
try{
Method m = entryclass.getMethod("getId");
memKey = (String) m.invoke(args[0]);//取属性名为id作为key
}catch(Exception e){
log.warn("{}中没有id属性",args[0]);
}
}
}
}
}//end if entryclass != null
}
if(memKey == null){
log.warn("方法{}运行时没有取到缓存key!",methodName);
return pjp.proceed(args);
}
//取缓存
memKey = getCacheKey(target, method.getName(), memKey);
if(needCache){
Object value = memcachedClient.get(memKey);
if(value != null){
value = JSONObject.parseObject(value.toString(), method.getGenericReturnType());
log.info("get data from cache:{} costTime:{}", memKey ,cost.cost());
return value;
}else{
Object obj = pjp.proceed(args);
/* if(obj instanceof NormalReturn){//不缓存错误结果
if(((NormalReturn) obj).getStatusCode().equals("200"))
return obj;
((NormalReturn) obj).setCache(true);//标记为已缓存
}*/
int timeout = MemcachedUtil.MEMCACHED_EXPIRY_TIME;//默认10分钟刷新
//boolean cached = memcachedClient.set(key, JSONObject.toJSONString(obj, SerializerFeature.BrowserCompatible),timeout);
boolean cached = asycSetCache(memKey, obj, timeout);
/* if(!cached && obj instanceof NormalReturn)
((NormalReturn) obj).setCache(false);//标记为未缓存
*/ log.info("cached:{} timeout:{} costTime:{}", memKey,timeout,cost.cost());
return obj;
}
}
//刷新缓存
if(flushCache){
List<String> methodList = new ArrayList<String>();
//取要刷新的key前缀,取不到则刷新所有带有@MemCaching注解的方法
String flushKey = method.getAnnotation(MemFlush.class).key();
if(StringUtils.isNotEmpty(flushKey)){
String[] str1=flushKey.split(",");
for(int i=0;i<str1.length;i++){
methodList.add(str1[i]);
}
}else{
Method[] methods = target.getClass().getDeclaredMethods();
for(Method mhd : methods){
if(mhd.isAnnotationPresent(MemCaching.class)){
methodList.add(mhd.getName());
}
}
}
if(memKey != null){
for(String list : methodList){
memKey = getCacheKey(target, list, memKey);
log.info("flush cache:{}", memKey);
//memcachedClient.delete(memKey);
asycDelCache(memKey);
}
}
}
return pjp.proceed(args);
}
/**
* 异步存入缓存
* @param key
* @param obj
* @param timeout
* @return
*/
public boolean asycSetCache(final String key,final Object obj,final int timeout) {
Callable<Boolean> cmd = new Callable<Boolean>(){
@Override
public Boolean call() throws Exception {
return memcachedClient.set(key, JSONObject.toJSONString(obj, SerializerFeature.BrowserCompatible),timeout);
}
};
ExecutorFactory.fixedExecutor.submit(cmd);
return true;
}
/**
* 异步删除缓存
* @param key
* @return
*/
public boolean asycDelCache(final String key) {
Callable<Boolean> cmd = new Callable<Boolean>(){
@Override
public Boolean call() throws Exception {
return memcachedClient.delete(key);
}
};
ExecutorFactory.fixedExecutor.submit(cmd);
return true;
}
private Method getMethod(Object target,String methodName,Object[] args){
Method method = null;
Class<?>[] classes=new Class<?>[args.length];
try {
for(int i=0,j=args.length;i<j;i++){
if(args[i]!=null){
classes[i]=args[i].getClass();
if(args[i] instanceof Map){
classes[i] = Map.class;
}
}else{
;
}
}
//method = target.getClass().getMethod(methodName, classes);
} catch (Exception e) {
method=null;
}
if(method == null){
Method ms[] = target.getClass().getDeclaredMethods();
for(int i=0;i<ms.length;i++){//遍历方法
Class<?>[] parameterTypes = ms[i].getParameterTypes();
if(ms[i].getName().equals(methodName) && parameterTypes.length == classes.length){
Boolean parmIsMatch = true;
for(int j=0;j<parameterTypes.length;j++){//遍历参数
if(classes[j]!=null && !ClassUtils.isAssignable( parameterTypes[j], classes[j] , true) ){//参数类型比对,忽略null参数
parmIsMatch = false;
break;
}
}
if(parmIsMatch)
return ms[i];
}
}
}
return method;
}
private String getCacheKey(Object target,String method,String arg){
StringBuilder sb=new StringBuilder();
sb.append(MemcachedUtil.MEMCACHED_PRE);
sb.append(ClassUtils.getShortClassName(target.getClass())).append("_").append(method);
sb.append("_").append(arg.hashCode());//get hash code arg too long
return sb.toString();
}
}