基于Redis的Service缓存实现

项目中有使用到缓存,每次需要将缓存代码和业务代码杂糅在一起,以及分散各处的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、应用案例:

基于Redis的Service缓存实现

 

上一篇:C#中烦人的Null值判断竟然这样就被消灭了


下一篇:Spring Boot 数据校验@Valid+统一异常处理