首先
创建一个注解接口,也就是接口继承于Annotation。设置两个参数,一个过期时间,一个缓存标识。如下:
// Type 代表可以放在类上, method 代表可以放在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {
long expire() default 1 * 60 * 1000;
// 缓存标识 key
String name() default "";
}
导入aop依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
编写切面代码
详细代码如下:
// aop 定义一个切面,切面定义了切点和通知的关系
@Component
@Slf4j
@Aspect
public class CacheAspect {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Pointcut("@annotation(com.itaasee.myblog.common.cache.Cache)") // 设置切点,只要有这个注解的地方就执行该aop方法
public void pointCut() {
}
@Around("pointCut()") // 环绕通知
public Object around(ProceedingJoinPoint joinPoint) {
try {
Signature signature = joinPoint.getSignature();
// 类名
String className = joinPoint.getTarget().getClass().getSimpleName();
// 方法名
String methodName = signature.getName();
Class[] parameterTypes = new Class[joinPoint.getArgs().length];
Object[] args = joinPoint.getArgs();
// 参数
String params = "";
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
params += JSON.toJSONString(args[i]);
parameterTypes[i] = args[i].getClass();
} else {
parameterTypes[i] = null;
}
}
if (StringUtils.isNotBlank(params)) {
// 加密 以防止出现key过长以及字符转义获取不到的情况
params = DigestUtils.md5Hex(params);
}
Method method = joinPoint.getSignature().getDeclaringType().getMethod(methodName, parameterTypes);
// 获取Cache注解
Cache annotation = method.getAnnotation(Cache.class);
// 缓存过期时间
long expire = annotation.expire();
// 缓存名称
String name = annotation.name();
// 先从redis中查询
String redisKey = name + "::" + className + "::" + methodName + "::" + params;
log.info("redisKey的值:{}", redisKey);
Boolean isExist = stringRedisTemplate.hasKey(redisKey);
if (Boolean.TRUE.equals(isExist)){
String redisValue = stringRedisTemplate.opsForValue().get(redisKey);
log.info("redisValue的值:{}", redisValue);
// 不为空说明缓存中有数据
log.info("走了缓存~~~,{},{}", className, methodName);
// 查询到数据就增长时间,考虑到数据更新问题不使用
// stringRedisTemplate.expire(redisKey,Duration.ofMillis(expire));
return JSON.parseObject(redisValue, Result.class);
}
// 调用正常方法,去数据库查数据
Object proceed = joinPoint.proceed();
// stringRedisTemplate.opsForValue().set(redisKey, JSONUtil.toJsonStr(proceed), Duration.ofMillis(expire));
// 使用new ObjectMapper().writeValueAsString的目的是防止Long型传递给前端出现精度丢失问题,将其转换为String类型
stringRedisTemplate.opsForValue().set(redisKey, new ObjectMapper().writeValueAsString(proceed), Duration.ofMillis(expire));
log.info("存入缓存~~~,{},{}", className, methodName);
return proceed;
} catch (Throwable e) {
e.printStackTrace();
}
return Result.fail(-999, "系统错误");
}
}