项目中有使用到缓存,每次需要将缓存代码和业务代码杂糅在一起,以及分散各处的key,严重影响代码的可读性。以下是使用AOP对其简单尝试。直接上代码:
1、定义缓存注解:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Cache { long timeOut() default 0; TimeUnit timeUnit() default TimeUnit.HOURS; }
2、定义参数唯一键注解,使用此注解标记此输入参数参与构成唯一键:
@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface CacheUniqueKey { }
3、CacheAspect
@Component @Slf4j @Aspect @Data public class CacheAspect { private final CacheCenter<String> cacheCenter; // 缓存开关配置 @Value("${service.cache}") private boolean cacheEnable = false; @Around(value = "@annotation(com.yingying.survey.component.cache.Cache)&&@annotation(cache)") public Object around(ProceedingJoinPoint joinPoint, Cache cache) throws Throwable { if (!cacheEnable) { return joinPoint.proceed(); } long timeOut = cache.timeOut(); TimeUnit timeUnit = cache.timeUnit(); if (timeOut == 0) { return joinPoint.proceed(); } Class<?> clazz = joinPoint.getTarget().getClass(); String clazzSimpleName = clazz.getSimpleName(); String methodName = joinPoint.getSignature().getName(); Signature signature = joinPoint.getSignature(); Class declaringType = ((MethodSignature) signature).getReturnType(); String uniqueParam = methodParamsResolve(joinPoint); String cacheKey = clazzSimpleName + ":" + methodName + ":" + uniqueParam; if (cacheCenter.isExistCache(cacheKey)) { String loadCache = cacheCenter.loadCache(cacheKey); log.info("data from cache:{}", loadCache); if (declaringType.isArray()) { return JSONArray.parseArray(loadCache, declaringType); } else { return JSON.parse(loadCache, declaringType); } } Object proceedResult = joinPoint.proceed(); // 为了从缓存中取值时不出现空指针的情况,现不对返回值为空的结果缓存。 if (proceedResult != null) { String cacheData = JSON.json(proceedResult); CacheUnit cacheUnit = new CacheUnit().setTimeUnit(timeUnit).setCacheOutTime(timeOut).setCacheKey(cacheKey); cacheCenter.insertCache(cacheUnit, cacheData); } return proceedResult; } private String methodParamsResolve(ProceedingJoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); Method method = ((MethodSignature) signature).getMethod(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); String uniqueParam = ""; int idx = 0; for (Annotation[] annotations : parameterAnnotations) { for (Annotation annotation : annotations) { if (annotation instanceof CacheUniqueKey) { Object[] args = joinPoint.getArgs(); uniqueParam = (String) args[idx]; return uniqueParam; } } idx++; } return uniqueParam; } }
4、缓存配置单元:
@Data @Accessors(chain = true) public class CacheUnit { @NotBlank @NotNull private String cacheKey; private long cacheOutTime = 0; @NotNull private TimeUnit timeUnit; }
5、缓存中心实现接口:
public interface CacheCenter<T> { boolean isExistCache(@NotNull @NotBlank String cacheKey); T loadCache(@NotNull @NotBlank String cacheKey); boolean insertCache(@NotNull CacheUnit cacheUnit, @NotNull T cacheData); }
6、基于Redis的缓存中心实现:
@Service("cacheCenter") @Data public class RedisCacheCenter implements CacheCenter<String> { private final RedisService redisService; @Override public boolean isExistCache(@NotNull @NotBlank String cacheKey) { return redisService.exists(cacheKey); } @Override public String loadCache(@NotNull @NotBlank String cacheKey) { return redisService.get(cacheKey); } @Override public boolean insertCache(@NotNull CacheUnit cacheUnit, @NotNull String cacheData) { long cacheOutTime = TimeUnit.SECONDS.convert(cacheUnit.getCacheOutTime(), cacheUnit.getTimeUnit()); redisService.set(cacheUnit.getCacheKey().getBytes(), cacheData.getBytes(), cacheOutTime); return true; } }
7、应用案例: