|
|
package com.yoho.activity.common.cache;
|
|
|
|
|
|
import java.lang.reflect.Method;
|
|
|
import java.util.LinkedList;
|
|
|
import java.util.List;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
|
import org.apache.commons.lang.ArrayUtils;
|
|
|
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.aspectj.lang.reflect.MethodSignature;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import com.yoho.activity.common.redis.CacheKeyHelper;
|
|
|
import com.yoho.core.common.utils.MD5;
|
|
|
import com.yoho.core.redis.YHRedisTemplate;
|
|
|
import com.yoho.core.redis.YHValueOperations;
|
|
|
|
|
|
@Aspect
|
|
|
@Component
|
|
|
public class CacheAop {
|
|
|
|
|
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(CacheAop.class);
|
|
|
|
|
|
private static final String DEFAULT_KEY_PRE = "yh:activity:";
|
|
|
private static final int DEFAULT_CACHE_SECONDS = 300;
|
|
|
|
|
|
@Resource(name = "yhValueOperations")
|
|
|
private YHValueOperations<String, String> yhValueOperations;
|
|
|
|
|
|
@Resource(name = "yhRedisTemplate")
|
|
|
private YHRedisTemplate<String, String> yhRedisTemplate;
|
|
|
|
|
|
@Pointcut("@annotation(com.yoho.activity.common.cache.Cacheable)")
|
|
|
public void cacheAnnotationPointCut() {
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Around("cacheAnnotationPointCut() ")
|
|
|
public Object cacheAop(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
|
|
|
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
|
|
|
|
|
Method method = signature.getMethod();
|
|
|
|
|
|
Method proxyMethod = ((MethodSignature)joinPoint.getSignature()).getMethod();
|
|
|
Method soruceMethod = joinPoint.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
|
|
|
|
|
|
|
|
|
Object[] args = joinPoint.getArgs();
|
|
|
Class<?> returnType = method.getReturnType();
|
|
|
Cacheable cachable = soruceMethod.getAnnotation(Cacheable.class);
|
|
|
String key = getCacheKey(soruceMethod, args);
|
|
|
|
|
|
logger.debug("Enter cache pointcut: key is {}", key);
|
|
|
|
|
|
//从缓存中获取数据
|
|
|
try {
|
|
|
Object result = getFromCache(key, returnType);
|
|
|
if (result != null) {
|
|
|
logger.debug("Cache hit for key:{}.", key);
|
|
|
return result;
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
logger.warn("get from cache exception.", e);
|
|
|
}
|
|
|
|
|
|
// cache miss
|
|
|
Object result = joinPoint.proceed();
|
|
|
try {
|
|
|
saveToCache(key, result, cachable.expireTime());
|
|
|
} catch (Exception e) {
|
|
|
logger.warn("save cache exception.", e);
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
private void saveToCache(String key, Object value, long expire) {
|
|
|
if (expire <= 0) {
|
|
|
expire = DEFAULT_CACHE_SECONDS;
|
|
|
}
|
|
|
yhValueOperations.set(key, CacheKeyHelper.value2String(value));
|
|
|
long timeout = 5;
|
|
|
yhRedisTemplate.longExpire(key, timeout, TimeUnit.SECONDS);
|
|
|
}
|
|
|
|
|
|
private Object getFromCache(String key, Class<?> valueType) {
|
|
|
return CacheKeyHelper.string2Value(yhValueOperations.get(key), valueType);
|
|
|
}
|
|
|
|
|
|
public void clearCache(Method method, Object[] args) {
|
|
|
yhRedisTemplate.delete(getCacheKey(method, args));
|
|
|
}
|
|
|
|
|
|
private String getCacheKey(Method method, Object[] args) {
|
|
|
|
|
|
Cacheable cachable = method.getAnnotation(Cacheable.class);
|
|
|
String keyPre = cachable.keyPre();
|
|
|
if (StringUtils.isBlank(keyPre)) {
|
|
|
keyPre = DEFAULT_KEY_PRE + method.getName();
|
|
|
}
|
|
|
int[] excludeParams = cachable.excludeParams();
|
|
|
String cacheValueKey = getValueStrings(args, excludeParams);
|
|
|
|
|
|
String cacheKey = keyPre + cacheValueKey;
|
|
|
|
|
|
// 最大250,超过限制需要MD5
|
|
|
if (cacheKey.length() > 250) {
|
|
|
cacheKey = MD5.md5(cacheKey);
|
|
|
}
|
|
|
|
|
|
return cacheKey;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取参数的签名
|
|
|
* public for test
|
|
|
* @param arguments arguments
|
|
|
* @param excludeParams arguments to exclude
|
|
|
* @return params sign
|
|
|
*/
|
|
|
public String getValueStrings(Object arguments[], int[] excludeParams) {
|
|
|
|
|
|
if (ArrayUtils.isEmpty(arguments)) {
|
|
|
return StringUtils.EMPTY;
|
|
|
}
|
|
|
|
|
|
List<String> methodParams = new LinkedList<>();
|
|
|
|
|
|
for (int i = 0; i < arguments.length; i++) {
|
|
|
//排除掉某些参数
|
|
|
if (ArrayUtils.contains(excludeParams, i)) {
|
|
|
continue;
|
|
|
}
|
|
|
Object arg = arguments[i];
|
|
|
String param = this.argToString(arg);
|
|
|
methodParams.add(param);
|
|
|
}
|
|
|
|
|
|
String arg_sign = String.join("-", methodParams).replaceAll("\\s+","");
|
|
|
|
|
|
return arg_sign;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 把参数对象转化为字符串,使用JSON转化
|
|
|
* A key can be up to 250 bytes. It may not contain:
|
|
|
* null (0x00)
|
|
|
* space (0x20)
|
|
|
* tab (0x09)
|
|
|
* newline (0x0a)
|
|
|
* carriage-return (0x0d)
|
|
|
*
|
|
|
* @param arg 参数对象
|
|
|
* @return 字符串
|
|
|
* @auth chunhua.zhang@yoho.cn
|
|
|
*/
|
|
|
private String argToString(Object arg) {
|
|
|
if (arg == null) {
|
|
|
return StringUtils.EMPTY;
|
|
|
}
|
|
|
if(arg instanceof String[]){
|
|
|
return String.join("-", (String[]) arg);
|
|
|
}else{
|
|
|
return String.valueOf(arg);
|
|
|
}
|
|
|
}
|
|
|
} |
...
|
...
|
|