使用自定義註解+AOP實現存放HASH類型的Redis緩存

使用自定義註解+AOP實現存放HASH類型的Redis緩存

1.自定義的Redis方法

	/**
     * @description 獲取返回值
     * @param key - 鍵 不能爲null
     * @param hashKey - 項 不能爲null
     * @return Object  -
     * @author XinLau
     * @since 2020/3/24 12:40
     * @creed The only constant is change ! ! !
     */
    public Object hget(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    /**
     * @description 獲取指定類型的返回值
     * @param key - 鍵 不能爲null
     * @param hashKey - 項 不能爲null
     * @param clazz - 返回值類型
     * @return <T> -
     * @author XinLau
     * @since 2020/3/24 15:44
     * @creed The only constant is change ! ! !
     */
    public <T> T hget(String key, String hashKey, Class<T> clazz) {
        Object hget = hget(key, hashKey);
        if (ConvertUtils.isEmpty(hget)){
            return null;
        }
        if (clazz.isInstance(hget)){
            return clazz.cast(hget);
        }
        return null;
    }

2.自定義註解

import java.lang.annotation.*;

/**
 * <p>
 * HashCache<br>
 *  以HASH形式存儲的緩存註解
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年03月24日 14:15
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HashCache {
    /**
     * HASH標識
     * @return
     */
    String hashIdentification();
    /**
     * HASH內存放的Key
     * @return
     */
    String hashKey();
    /**
     * HASH過期時間
     * 0 - 立即過期
     * -1 - 不會被使用
     * -2 - 永不過期
     * @return
     */
    int expireTime() default -2;
}
import java.lang.annotation.*;

/**
 * <p>
 * HashCacheEvict<br>
 *  清除以HASH形式存儲的緩存註解
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年03月25日 09:15
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HashCacheEvict {
    /**
     * HASH標識
     * @return
     */
    String hashIdentification();
    /**
     * HASH內存放的Key
     * @return
     */
    String hashKey();
}

import java.lang.annotation.*;

/**
 * <p>
 * HashCachePut<br>
 *  以HASH形式存儲的緩存註解
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年03月24日 14:15
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HashCachePut {
    /**
     * HASH標識
     * @return
     */
    String hashIdentification();
    /**
     * HASH內存放的Key
     * @return
     */
    String hashKey();
    /**
     * HASH過期時間
     * 0 - 立即過期
     * -1 - 不會被使用
     * -2 - 永不過期
     * @return
     */
    int expireTime() default -2;
}

3.自定義Aspect


import com.zzdz.platform.base.annotation.HashCache;
import com.zzdz.platform.base.annotation.HashCacheEvict;
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.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * <p>
 * HashCacheAspect<br>
 * 以HASH形式存儲的緩存註解 切面處理類
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年03月24日 14:37
 */
@Aspect
@Component
public class HashCacheAspect {
    /**
     * Redis 工具類
     */
    @Resource
    private com.zzdz.platform.base.util.RedisUtil redisUtil;

    /**
     * @description 獲取被攔截方法對象
     *  MethodSignature.getMethod() 獲取的是頂層接口或者父類的方法對象
     *  而緩存的註解在實現類的方法上
     *  所以應該使用反射獲取當前對象的方法對象
     * @param proceedingJoinPoint - ProceedingJoinPoint
     * @return Method -
     * @author XinLau
     * @since 2020/3/24 14:58
     * @creed The only constant is change ! ! !
     */
    private Method getMethod(ProceedingJoinPoint proceedingJoinPoint) {
//        獲取參數的類型
        Object[] args = proceedingJoinPoint.getArgs();
        Class[] argTypes = new Class[proceedingJoinPoint.getArgs().length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }
        Method method = null;
        try {
            method = proceedingJoinPoint.getTarget().getClass().getMethod(proceedingJoinPoint.getSignature().getName(), argTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return method;
    }

    /**
     * @description 獲取緩存的hashKey
     * @param hashKey - hashKey
     * @param method -
     * @param args -方法參數
     * @return String -
     * @author XinLau
     * @since 2020/3/24 15:48
     * @creed The only constant is change ! ! !
     */
    private String parseKey(String hashKey, Method method, Object[] args) {
//        1.獲取被攔截方法參數名列表(使用Spring支持類庫)
        LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = localVariableTableParameterNameDiscoverer.getParameterNames(method);
//        2.使用SPEL進行key的解析
        ExpressionParser parser = new SpelExpressionParser();
//        3.SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
//        4.把方法參數放入SPEL上下文中
        for (int i = 0; i < paraNameArr.length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(hashKey).getValue(context, String.class);
    }

    /**
     * @description 緩存邏輯
     * @param proceedingJoinPoint - ProceedingJoinPoint
     * @return Object - 返回值
     * @author XinLau
     * @since 2020/3/24 15:35
     * @creed The only constant is change ! ! !
     */
    @Around("@annotation(com.zzdz.platform.base.annotation.HashCache)")
    public Object hashCache(ProceedingJoinPoint proceedingJoinPoint) {
//        1.獲取被攔截方法對象
        Method method = getMethod(proceedingJoinPoint);
//        2.獲取 Annotation
        HashCache hashCache = method.getAnnotation(HashCache.class);
//        3.獲取HASH的HASH標識
        String hashIdentification = hashCache.hashIdentification();
//        存放的Key
        String hashKey = parseKey(hashCache.hashKey(), method, proceedingJoinPoint.getArgs());
//        過期時間
        int expireTime = hashCache.expireTime();
//        4.獲取方法的返回類型,讓緩存可以返回正確的類型
        Class returnType = ((MethodSignature) proceedingJoinPoint.getSignature()).getReturnType();
//        5.使用 Redis 的 HASH 進行存取,易於管理
        Object result = redisUtil.hget(hashIdentification, hashKey, returnType);
        if (result == null){
//            緩存爲空
            try {
//                6.獲取使用註解的方法的返回值
                result = proceedingJoinPoint.proceed();
                Assert.notNull(hashKey, "HASH內存放的Key");
//                7.緩存存放
                if (expireTime >= 0){
//                    帶過期時間(包括0 - 立即過期)
                    redisUtil.hset(hashIdentification, hashKey, result, expireTime);
                }else {
//                    不帶過期時間
                    redisUtil.hset(hashIdentification, hashKey, result);
                }
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
        return result;
    }

    /**
     * @description 緩存清除
     * @param proceedingJoinPoint - ProceedingJoinPoint
     * @return Object - 返回值
     * @author XinLau
     * @since 2020/3/25 09:35
     * @creed The only constant is change ! ! !
     */
    @Around("@annotation(com.zzdz.platform.base.annotation.HashCacheEvict)")
    public void hashCacheEvict(ProceedingJoinPoint proceedingJoinPoint) {
//        1.獲取被攔截方法對象
        Method method = getMethod(proceedingJoinPoint);
//        2.獲取 Annotation
        HashCacheEvict hashCacheEvict = method.getAnnotation(HashCacheEvict.class);
//        3.獲取HASH的HASH標識
        String hashIdentification = hashCacheEvict.hashIdentification();
//        過期時間
        String hashKey = hashCacheEvict.hashKey();
//        4.獲取方法的返回類型,讓緩存可以返回正確的類型
        Class returnType = ((MethodSignature) proceedingJoinPoint.getSignature()).getReturnType();
//        5.使用 Redis 的 HASH 進行存取,易於管理
        Object result = redisUtil.hget(hashIdentification, hashKey);
        if (result == null){
            return;
        }
        redisUtil.del(hashIdentification, hashKey);
    }

    /**
     * @description 緩存修改
     * @param proceedingJoinPoint - ProceedingJoinPoint
     * @return Object - 返回值
     * @author XinLau
     * @since 2020/3/24 15:35
     * @creed The only constant is change ! ! !
     */
    @Around("@annotation(com.zzdz.platform.base.annotation.HashCachePut)")
    public Object hashCachePut(ProceedingJoinPoint proceedingJoinPoint) {
//        1.獲取被攔截方法對象
        Method method = getMethod(proceedingJoinPoint);
//        2.獲取 Annotation
        HashCache hashCache = method.getAnnotation(HashCache.class);
//        3.獲取HASH的HASH標識
        String hashIdentification = hashCache.hashIdentification();
//        存放的Key
        String hashKey = parseKey(hashCache.hashKey(), method, proceedingJoinPoint.getArgs());
//        過期時間
        int expireTime = hashCache.expireTime();
        Object result = null;
        try {
//            4.獲取使用註解的方法的返回值
            result = proceedingJoinPoint.proceed();
            Assert.notNull(hashKey, "HASH內存放的Key");
//            5.緩存存放
            if (expireTime >= 0){
//                    帶過期時間(包括0 - 立即過期)
                redisUtil.hset(hashIdentification, hashKey, result, expireTime);
            }else {
//                    不帶過期時間
                redisUtil.hset(hashIdentification, hashKey, result);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章