注解实现Redis缓存,使用SpringEL表达式
注解类
package com.nsk666.common.annotation;
import java.lang.annotation.*;
/**
* @author niushuaikui
* @description 自定义redis注解
* @date 2023/8/2
*/
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Redis {
// 名字前缀
String nameSpace() default "";
// key
String key();
// 过期时间
int expireTime() default 60 * 1000;
// 是否是查询操作,如果是操作数据库设置为false
boolean read() default true;
}
切面类
package com.nsk666.framework.aspectj;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.nsk666.common.annotation.Redis;
import com.nsk666.common.core.redis.RedisCache;
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.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
/**
* @author niushuaikui
* @description Redis缓存切面类
* @date 2023/8/2
*/
@Aspect
@Component
public class RedisCacheAspect {
private static final Logger log = LoggerFactory.getLogger(RedisCacheAspect.class);
@Resource
RedisCache redisCache;
@Pointcut(value = "@annotation(com.nsk666.common.annotation.Redis)")
public void redisCache() {
}
@Around("redisCache()")
private Object saveCache(ProceedingJoinPoint point) throws Throwable {
// 获取切入方法对象
// 获取代理对象的方法,不包含注解
Method method = ((MethodSignature) point.getSignature()).getMethod();
// 获取目标对象的方法,包含注解
Method methodWithAnnotation = point.getTarget().getClass()
.getDeclaredMethod(point.getSignature().getName(), method.getParameterTypes());
Object result = null;
// 获取目标方法的注解对象
Redis annotation = methodWithAnnotation.getAnnotation(Redis.class);
// 解析key
String key = parseKey(methodWithAnnotation, point.getArgs(), annotation);
boolean read = annotation.read();
// 非读,获取方法执行结果,存入缓存
if (!read) {
result = point.proceed();
// 缓存过期时间
int expireTime = annotation.expireTime();
redisCache.setCacheObject(key, JSONObject.toJSONString(result), expireTime);
return result;
}
// 读,则直接从缓存获取,获取不到从数据库获取。
String cacheObject = redisCache.getCacheObject(key);
if (cacheObject == null) {
// Redis中不存在,放注解往下执行,从方法中获取结果
result = point.proceed();
// 方法返回结果不为空
if (result != null) {
// 缓存过期时间
int expireTime = annotation.expireTime();
redisCache.setCacheObject(key, JSONObject.toJSONString(result), expireTime);
return result;
} else {
log.error(key + ",在缓存中未找到,在数据库中未找到");
return null;
}
} else {
return deSerialize(methodWithAnnotation, cacheObject);
}
}
/**
* 反序列化
*
* @param m 原方法的对应信息
* @param cache 缓存字符串
*/
private Object deSerialize(Method m, String cache) {
// 原方法的返回数据类型类
Class<?> returnTypeClass = m.getReturnType();
Object object = null;
// 原方法的返回数据类型类
Type returnType = m.getGenericReturnType();
// 判断是否是ParameterizedType的实例,即泛型
if (returnType instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType) returnType;
Type[] typeArguments = type.getActualTypeArguments();
for (Type typeArgument : typeArguments) {
// 如果是泛型则需要将其中每个单独转换
Class<?> typeArgClass = (Class<?>) typeArgument;
object = JSON.parseArray(cache, typeArgClass);
}
} else {
// 不是泛型则直接转换
object = JSON.parseObject(cache, returnTypeClass);
}
return object;
}
/**
* 解析注解中的key,命名空间+key+方法名+参数的MD5加密
*
* @param method 使用注解的方法
* @param argValues 方法参数
* @param annotation 注解
*/
private String parseKey(Method method, Object [ ] argValues, Redis annotation) {
// ================================方式1.通过自定义EL表达式缓存key===========================
String key = annotation.key();
// 判断key是否是el表达式
if (key.startsWith("#")) {
// 表达式解析器
ExpressionParser parser = new SpelExpressionParser();
// 解析表达式
Expression expression = parser.parseExpression(annotation.key());
// 构建表达式上下文
StandardEvaluationContext context = new StandardEvaluationContext();
// 获取指定方法的方法信息
DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
// 获取指定方法的参数名
String [ ] paramNames = defaultParameterNameDiscoverer.getParameterNames(method);
// 遍历参数名,把参数赋值给参数名
if (paramNames != null && paramNames.length > 0) {
for (int i = 0; i < paramNames.length; i++) {
context.setVariable(paramNames [i ], argValues [i ]);
}
}
// 经过表达式解析,在上下文寻找能匹配的值 user.id => 传入的参数名为user 且对应的对象有id属性
Object o = expression.getValue(context, Object.class);
if (null == o) {
log.error("El表达式" + key + ":未匹配" + Arrays.toString(paramNames));
return annotation.nameSpace() + ":" + key.replace("#","");
}
return annotation.nameSpace() + JSONObject.toJSONString(o);
} else {
return annotation.nameSpace() + ":" + key;
}
// ===================================方式2.通过加密混淆key缓存======================================
// StringBuilder prefix = new StringBuilder();
// prefix.append(annotation.nameSpace()).append(".").append(annotation.key());
// prefix.append(".").append(method.getName());
// StringBuilder sb = new StringBuilder();
// for (Object obj : argValues) {
// sb.append(obj.toString());
// }
// return prefix.append(DigestUtils.md5DigestAsHex(sb.toString().getBytes())).toString();
}
}
测试
@RequestMapping("/test")
@Redis(key = "#a")
public String test(String a,String b){
System.out.println(a);
System.out.println(b);
return a+b;
}
@RequestMapping("/test1")
@Redis(key = "#wxUser.id")
public WxUser test1(@RequestBody WxUser wxUser){
System.out.println(wxUser);
return wxUser;
}