SpringBoot 註解呼叫Redis快取

weixin_34007886發表於2018-09-30

註解程式碼:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by qhong on 2018/9/5 11:12
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface CreditRedisCache {
    String prefix() default "huishi-server:credit";
}

利用攔截器處理註解中的方法,有就呼叫快取,沒有就新增

import com.alibaba.fastjson.JSON;
import com.shitou.huishi.annotation.CreditRedisCache;
import com.shitou.huishi.utils.RedisUtil;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Created by qhong on 2018/9/5 11:13
 **/
@Aspect
@Component
@Slf4j
public class RedisCacheAspect {

    @Autowired
    RedisUtil redisUtil;

    /**
     * 分隔符 生成key 格式為 類全類名|方法名|引數所屬類全類名
     **/
    private static final String DELIMITER = "-";

    private static final Long expireTime=60*60*24*30L;

    /**
     * Service層切點 使用到了我們定義的 RedisCache 作為切點表示式。
     * 而且我們可以看出此表示式基於 annotation。
     * 並且用於內建屬性為查詢的方法之上
     */
    @Pointcut("@annotation(com.shitou.huishi.annotation.CreditRedisCache)")
    public void redisCacheAspect() {
    }

    /**
     * Around 手動控制呼叫核心業務邏輯,以及呼叫前和呼叫後的處理,
     * <p>
     * 注意:當核心業務拋異常後,立即退出,轉向AfterAdvice 執行完AfterAdvice,再轉到ThrowingAdvice
     *
     * @param pjp the pjp
     * @return object
     * @throws Throwable the throwable
     */
    @Around(value = "redisCacheAspect()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        // 得到類名、方法名和引數
        String clazzName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();

        // 根據類名、方法名和引數生成Key
        log.info("key引數: " + clazzName + "." + methodName);
        String key = getKey(clazzName, methodName, args);
        if (log.isInfoEnabled()) {
            log.info("生成key: " + key);
        }

        // 得到被代理的方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();

        //redis 字首
        String prefix = method.getAnnotation(CreditRedisCache.class).prefix();

        // 檢查Redis中是否有快取
        Object value =  redisUtil.get(prefix, key);

        // 得到被代理方法的返回值型別
        Class returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();

        // result是方法的最終返回結果
        Object result = null;
        try {
            if (null == value) {
                log.info("快取未命中");
                // 呼叫資料庫查詢方法
                result = joinPoint.proceed(args);
                // 結果放入快取
                redisUtil.set(prefix, key, result,expireTime);
            } else {
                // 快取命中
                log.info("快取命中, value = " + JSON.toJSONString(value));
                result = value;
            }
        } catch (Throwable e) {
            log.error("程式異常",e.getMessage());
            throw e;
        }
        return result;
    }

    /**
     *      * 根據類名、方法名和引數生成Key
     *      * @param clazzName
     *      * @param methodName
     *      * @param args
     *      * @return key格式:全類名|方法名|引數型別
     *
     */
    private String getKey(String clazzName, String methodName, Object[] args) {
        StringBuilder key = new StringBuilder(clazzName);
        key.append(DELIMITER);
        key.append(methodName);
        key.append(DELIMITER);
        key.append(Arrays.stream(args).map(x->x.toString()).collect(Collectors.joining(DELIMITER)));
        return key.toString();
    }

}

使用:

    @CreditRedisCache
    public DataResponse queryICInfo(String name,String card)

直接在方法上使用即可,如果要自定義字首,可以新增prefix,不然使用預設值。

這種很類似Spring-Cache,但是自己的程式碼比較靈活 ,可以針對不同的模組設定字首,快取時間等。

參考:

https://www.jianshu.com/p/95ddef3168f8

https://www.cnblogs.com/hongdada/p/9263699.html

相關文章