9 changed files with 215 additions and 4 deletions
@ -0,0 +1,35 @@ |
|||||
|
package com.qs.serve.common.framework.annotations; |
||||
|
|
||||
|
import java.lang.annotation.*; |
||||
|
|
||||
|
/** |
||||
|
* 解决原有@CacheAble不支持独立失效策略场景 |
||||
|
* @author YenHex |
||||
|
* @since 2024/10/17 |
||||
|
*/ |
||||
|
@Target({ElementType.METHOD}) |
||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||
|
@Documented |
||||
|
public @interface RedisCacheable { |
||||
|
|
||||
|
/** |
||||
|
* redis缓存前缀,默认类与方法名 |
||||
|
* @return |
||||
|
*/ |
||||
|
String prefix() default ""; |
||||
|
|
||||
|
/** |
||||
|
* 过期时间 |
||||
|
* @return |
||||
|
*/ |
||||
|
int expire() default 350; |
||||
|
|
||||
|
/** |
||||
|
* 缓存读取主键表达式 |
||||
|
* @return |
||||
|
*/ |
||||
|
String expression() default ""; |
||||
|
|
||||
|
String SIMPLE_KEY = "#key"; |
||||
|
|
||||
|
} |
@ -0,0 +1,135 @@ |
|||||
|
package com.qs.serve.common.framework.aop; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.toolkit.StringPool; |
||||
|
import com.qs.serve.common.framework.annotations.RedisCacheable; |
||||
|
import com.qs.serve.common.framework.redis.RedisService; |
||||
|
import com.qs.serve.common.util.JsonUtil; |
||||
|
import com.qs.serve.common.util.StringUtils; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
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.core.annotation.AnnotatedElementUtils; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.lang.reflect.Method; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
/** |
||||
|
* @author YenHex |
||||
|
* @since 2024/10/18 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Aspect |
||||
|
@Component |
||||
|
@AllArgsConstructor |
||||
|
public class RedisCacheAspect { |
||||
|
|
||||
|
private final RedisService redisService; |
||||
|
|
||||
|
@Pointcut("@annotation(com.qs.serve.common.framework.annotations.RedisCacheable)") |
||||
|
public void PointCut() {} |
||||
|
|
||||
|
@Around("PointCut()") |
||||
|
public Object redisCacheAdvice(ProceedingJoinPoint joinPoint) throws Throwable { |
||||
|
Object[] args = joinPoint.getArgs(); |
||||
|
Signature signature = joinPoint.getSignature(); |
||||
|
MethodSignature methodSignature = ((MethodSignature) signature); |
||||
|
Method method = methodSignature.getMethod(); |
||||
|
RedisCacheable redisCacheable = AnnotatedElementUtils.findMergedAnnotation(method, RedisCacheable.class); |
||||
|
if(redisCacheable!=null){ |
||||
|
String prefix = StringUtils.hasText(redisCacheable.prefix()) ? redisCacheable.prefix() |
||||
|
: methodSignature.getDeclaringType().getName() +"."+ methodSignature.getMethod().getName()+"()"; |
||||
|
//构建表达式key
|
||||
|
String pk = this.getPrimaryKey(methodSignature,redisCacheable,args); |
||||
|
String redisCacheKey = "RCache:" + prefix + (pk==null?"":pk); |
||||
|
Object result = redisService.get(redisCacheKey); |
||||
|
if(result!=null){ |
||||
|
return JsonUtil.jsonToMap(result.toString()); |
||||
|
} |
||||
|
result = joinPoint.proceed(args); |
||||
|
if(result!=null){ |
||||
|
redisService.set(redisCacheKey,JsonUtil.objectToJson(result),redisCacheable.expire(), TimeUnit.MILLISECONDS); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
// 返回默认对象
|
||||
|
return joinPoint.proceed(args); |
||||
|
} |
||||
|
|
||||
|
public String getPrimaryKey(MethodSignature methodSignature,RedisCacheable annotation,Object[] args){ |
||||
|
String method = methodSignature.getDeclaringType().getName() |
||||
|
+"."+ methodSignature.getMethod().getName()+"()"; |
||||
|
final String expression = annotation.expression(); |
||||
|
//适配表达式读取key,key读取不到log.error,放行请求
|
||||
|
String primaryKey = null; |
||||
|
if(StringUtils.isNotEmpty(expression)){ |
||||
|
//
|
||||
|
if(expression.equals(RedisCacheable.SIMPLE_KEY)&&args.length==1){ |
||||
|
return args[0].toString(); |
||||
|
} |
||||
|
List<String> keys = new ArrayList<>(); |
||||
|
// 指定表名
|
||||
|
String tableName = null; |
||||
|
if(expression.contains(StringPool.DOT)){ |
||||
|
String[] keyArr = expression.split(StringPool.BACK_SLASH + StringPool.DOT); |
||||
|
for (String key : keyArr) { |
||||
|
if(key.contains(StringPool.DOLLAR)){ |
||||
|
tableName = key.replace(StringPool.DOLLAR,""); |
||||
|
continue; |
||||
|
} |
||||
|
keys.add(key); |
||||
|
} |
||||
|
}else { |
||||
|
keys.add(expression); |
||||
|
} |
||||
|
for (Object arg : args) { |
||||
|
// 跳过非指定的表名
|
||||
|
if(tableName!=null){ |
||||
|
String clazzName = arg.getClass().getSimpleName(); |
||||
|
if(!clazzName.equals(tableName)){ |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
// 读取key
|
||||
|
primaryKey = getPrimaryKeyRecursion(keys, arg, 0); |
||||
|
} |
||||
|
if(primaryKey==null){ |
||||
|
log.error("LockSubmitAspect失效,方法:{} 无法读取主键:{}",method,expression); |
||||
|
} |
||||
|
} |
||||
|
return primaryKey; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 递归读取主键 |
||||
|
* @param keys |
||||
|
* @param arg |
||||
|
* @param startIndex |
||||
|
* @return |
||||
|
*/ |
||||
|
private String getPrimaryKeyRecursion(List<String> keys, Object arg, int startIndex) { |
||||
|
String key = keys.get(startIndex); |
||||
|
Map<String,Object> param = JsonUtil.jsonToMap(JsonUtil.objectToJson(arg)); |
||||
|
Object val = param.get(key); |
||||
|
if(val!=null){ |
||||
|
boolean isLast = startIndex + 1== keys.size(); |
||||
|
if(isLast){ |
||||
|
return val.toString(); |
||||
|
}else { |
||||
|
// 自调支持无限层级
|
||||
|
return getPrimaryKeyRecursion(keys, val, startIndex + 1 ); |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
} |
Loading…
Reference in new issue