基于SpringAop与SpEL表达式实现缓存逻辑
背景
项目中使用到了大量缓存的场景,而每次书写设置与获取缓存数据的方法过于冗余,所以考虑使用切面来减少重复代码的书写
具体思路
- 自定义注解+Aop 减少重复代码书写
- SpEL表达式 实现对缓存键的动态设置
代码思路
注解代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.beastmouth.cache.aspect;
import com.beastmouth.cache.common.bean.constant.CacheRedisConstant;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Cacheable {
String cacheKey() default "";
long timeToLive() default 30L;
TimeUnit timeUnit() default TimeUnit.MINUTES; }
|
切面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| package com.beastmouth.cache.aspect;
import com.beastmouth.cache.component.wx.CacheableManager; import lombok.extern.slf4j.Slf4j; 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.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit;
@Slf4j @Aspect @Component public class CacheableAspect { @Resource private CacheableManager cacheableManager;
private SpelExpressionParser parser = new SpelExpressionParser();
@Around("@annotation(com.beastmouth.cache.aspect.Cacheable)") public Object cache(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); if (method == null) { return joinPoint.proceed(); } Cacheable cacheable = method.getAnnotation(Cacheable.class); if (cacheable == null) { return joinPoint.proceed(); } String cacheKey = cacheable.cacheKey(); cacheKey = parseKey(joinPoint, cacheKey); long timeToLive = cacheable.timeToLive(); TimeUnit timeUnit = cacheable.timeUnit(); if (StringUtils.isBlank(cacheKey)) { return joinPoint.proceed(); } Object cacheValue = cacheableManager.getCache(cacheKey, method); if (cacheValue != null) { log.info("[缓存切面] 命中缓存, key:{}", cacheKey); return cacheValue; } Object result = joinPoint.proceed(); cacheableManager.setCache(cacheKey, result, timeToLive, timeUnit); return result; }
private String parseKey(ProceedingJoinPoint joinPoint, String cacheKey) { EvaluationContext context = new StandardEvaluationContext(); Object[] args = joinPoint.getArgs(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); String[] parameterNames = methodSignature.getParameterNames(); for (int i = 0; i < args.length; i++) { context.setVariable(parameterNames[i], args[i]); } return parser.parseExpression(cacheKey).getValue(context, String.class); } }
|
具体Redis缓存数据的设置和读取 基于Redission
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package com.beastmouth.cache.component.wx;
import com.alibaba.fastjson.JSON; import org.apache.commons.lang3.StringUtils; import org.redisson.api.RBucket; import org.redisson.api.RedissonClient; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.concurrent.TimeUnit;
@Component public class CacheableManager { @Resource private RedissonClient redissonClient;
public void setCache(String key, Object value, Long timeToLive, TimeUnit timeUnit) { RBucket<String> bucket = redissonClient.getBucket(key); if (timeToLive != null) { bucket.set(JSON.toJSONString(value), timeToLive, timeUnit); } else { bucket.set(JSON.toJSONString(value)); } }
public Object getCache(String key, Method method) throws Exception { RBucket<String> bucket = redissonClient.getBucket(key); String result = bucket.get(); Class<?> returnType = method.getReturnType(); if (returnType.equals(List.class)) { Type clazz = method.getGenericReturnType(); Type type = ((ParameterizedType) clazz).getActualTypeArguments()[0]; return getList(Class.forName(type.getTypeName()), result); } else { return StringUtils.isBlank(result) ? null : JSON.parseObject(result, returnType); } }
private <T> List<T> getList(Class<T> clazz, String result) { return StringUtils.isBlank(result) ? null : JSON.parseArray(result, clazz); } }
|
业务代码的使用
1 2 3 4 5
| @Transactional(rollbackFor = Exception.class) @Cacheable(cacheKey = "T(com.beastmouth.cache.common.bean.constant.CacheRedisConstant).TEST + #param1 + ':' + #param2 + ':' + #param3 + '-' + #param4") public Long replyTimeLimitationUserId(String param1, String param2, Long param3, Long param4) { return 1L; }
|
- Post title:基于SpringAop与SpEL表达式实现缓存逻辑
- Post author:大黄
- Create time:2024-06-06 17:04:28
-
Post link:https://huangbangjing.cn/2024/06/06/基于SpringAop与SpEL表达式实现缓存逻辑/
-
Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.