1.先创建一个自定义注解需要用到@Retention 注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*@Description TODO 基于注解+Redis 实现方法级缓存
*@Version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodCache {
/**
* 指定缓存的过期时间, 默认60秒
*
* @return int
*/
int expireSeconds() default 60;
/**
* 缓存的key, 如果不指定, 默认按照方法的签名作为key
*
* @return String
*/
String key() default "";
/**
* 缓存开启标志 默认开启
*
* @return boolean
*/
boolean limitQuery() default true;
/**
* 说明
* @return
*/
String explain() default "";
}
上述注解定义好我们可以在方法上使用
@MethodCache(expireSeconds = 1200,key = "cacheKey",explain = "列表信息")
2.定义切面进入的标准利用上述建的MethodCache注解
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.application.core.util.domain.Response;
import com.factory.annotation.MethodCache;
import com.factory.util.HashAlgorithms;
import com.factory.util.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.net.HttpCookie;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @ClassName MethodCacheAspect
* @Description TODO 定义缓存切面
* @Version 1.0
*/
@Aspect
@Order(value = 2)
@Component
public class MethodCacheAspect {
@Resource
private RedisUtil redisUtil;
//定义MethodCacheKey唯一标识,方便对Key做统一的处理
private final String METHOD_CACHE_KEY = "MethodCacheKey-";
private static Logger log = LoggerFactory.getLogger(MethodCacheAspect.class);
/**
* 切面具体的操作
*
* @param proceedingJoinPoint 切面
* @param methodCache 注解
* @return Object
* @throws Throwable 抛出异常
*/
@Around("@annotation(methodCache)")
public Object execute(ProceedingJoinPoint proceedingJoinPoint, MethodCache methodCache) throws Throwable {
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = methodSignature.getMethod();
if(StringUtils.isNotEmpty(methodCache.explain())){
log.info("[MethodCache]加载方法{}",methodCache.explain());
}else{
log.info("[MethodCache]加载方法{}",method.getName());
}
// 解析请求参数
List<Object> list = parseRequestParam(proceedingJoinPoint);
// 根据方法获取相应的key
String key = methodCache.key();
if (StringUtils.isBlank(key)) {
key = getSignature(method);
}
key =METHOD_CACHE_KEY + key + HashAlgorithms.mixHash(JSON.toJSONString(list));
log.info("[MethodCacheKey]:{}",key);
Object deserialize = tryGetFromCache(key);
if (deserialize != null) {
return deserialize;
}
Object proceed;
if (methodCache.limitQuery()) {
proceed = executeConcreteMethod(proceedingJoinPoint);
redisUtil.set(key, JSON.toJSONString(proceed));
redisUtil.expire(key, methodCache.expireSeconds(),TimeUnit.SECONDS);
} else {
// 允许查询
proceed = executeConcreteMethod(proceedingJoinPoint);
}
return proceed;
}
/**
* 执行具体的方法
*
* @param proceedingJoinPoint 切面
* @return Object
* @throws Throwable 异常
*/
private Object executeConcreteMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
return proceedingJoinPoint.proceed();
}
/**
* 尝试从缓存中获取
*
* @param key key
* @return Object
*/
private Object tryGetFromCache(String key) {
if(!redisUtil.hasKey(key)){
return null;
}
String result = redisUtil.get(key);
// log.info("[MethodCache]读取缓存中信息:{}",result);
if(result != null){
return JSONObject.parseObject(result,Response.class);
}
return null;
}
/**
* 解析请求参数
*
* @param proceedingJoinPoint 切面
* @return List<Object>
*/
private List<Object> parseRequestParam(ProceedingJoinPoint proceedingJoinPoint) {
// 方法参数解析
int size = proceedingJoinPoint.getArgs().length;
Object[] args = proceedingJoinPoint.getArgs();
List<Object> argList = new ArrayList<>();
for (int i = 0; i < size; i++) {
if (args[i] instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) args[i];
argList.add(request.getParameterMap());
} else if (args[i] instanceof HttpServletResponse || args[i] instanceof HttpSession
|| args[i] instanceof HttpCookie) {
continue;
} else {
argList.add(args[i]);
}
}
return argList;
}
/**
* 生成方法签名
*
* @param method 方法
* @return String
*/
private String getSignature(Method method) {
StringBuilder sb = new StringBuilder();
String methodName = method.getName();
if (StringUtils.isNotBlank(methodName)) {
sb.append(method).append("#");
}
return sb.toString();
}
}
3.现在就可以使用注解了切面缓存查询了
//缓存查询
@MethodCache(expireSeconds = 1200,key = "cacheKey",explain = "列表")
@GetMapping(value = "findCacheKey")
public Response findCacheKey(@RequestParam(required = true) String channelCode,@RequestParam(required = true)String comCode) {
//上面用到@MethodCache注解 会先切面查询缓存 缓存没有查询到再进入此方法查询
}