SpringBoot使用AOP实现自定义接口缓存

一、引入pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、新建两个注解

(1)该注解表示需要缓存

package com.lzc.aopcache.annotation;

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 LzcCache {

    String cacheName() default "";

    String key() default "";

    long timeOut() default 0;

    TimeUnit timeUnit() default TimeUnit.HOURS;
}

(2)该注解表示需要清除缓存

package com.lzc.aopcache.annotation;

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 LzcCacheEvict {

    String cacheName() default "";

    String key() default "";

}

三、新建两个切面

(1)缓存切面

package com.lzc.aopcache.aspect;

import com.lzc.aopcache.annotation.LzcCache;
import com.lzc.aopcache.utils.JsonUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.StringRedisTemplate;
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 java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class LzcCacheAspect {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Around("execution(* com.lzc.aopcache.*.*.*(..))")
    public Object lzcCacheAspect(ProceedingJoinPoint jp) throws Throwable {

        Class<?> cls = jp.getTarget().getClass();
        String methodName = jp.getSignature().getName();

        Map<String, Object> map = isChache(cls, methodName);
        boolean isCache = (boolean)map.get("isCache");

        if (isCache) {
            String cacheName = (String) map.get("cacheName"); // 缓存名字
            String key = (String) map.get("key"); // 自定义缓存key
            long timeOut = (long) map.get("timeOut"); // 过期时间, 0代表永久有效
            TimeUnit timeUnit = (TimeUnit) map.get("timeUnit"); // 过期时间单位
            Class<?> methodReturnType = (Class<?>)map.get("methodReturnType"); // 方法的返回类型
            Method  method = (Method)map.get("method"); // 方法

            String realCacheName = "";
            // 判断cacheName是否为空,如果cacheName为空则使用默认的cacheName
            if(cacheName.equals("")) {
                realCacheName = cls.getName() + "." + methodName;
            } else {
                realCacheName = cacheName;
            }

            String realKey = "";
            // 判断key是否为空, 如果为空则使用默认的key
            if (key.equals("")) {
                realKey = realCacheName + "::" + defaultKeyGenerator(jp);
            } else {
                realKey = realCacheName + "::" + parseKey(key, method, jp.getArgs());
            }

            // 判断缓存中是否存在该key, 如果存在则直接从缓存中获取数据并返回
            if (stringRedisTemplate.hasKey(realKey)) {
                String value = stringRedisTemplate.opsForValue().get(realKey);
                return JsonUtil.toBean(methodReturnType, value);
            } else {
                Object result = jp.proceed();
                // 将返回结果保存到缓存中
                if (timeOut == 0) {
                    stringRedisTemplate.opsForValue().set(realKey, JsonUtil.toJson(result));
                } else {
                    stringRedisTemplate.opsForValue().set(realKey, JsonUtil.toJson(result), timeOut, timeUnit);
                }
                return result;
            }
        }
        return jp.proceed();
    }
    /**
     * 自定义生成key,使用方法中的参数作为key
     */
    private String defaultKeyGenerator(ProceedingJoinPoint jp) {
        // 获取所有参数的值
        List<String> list = new ArrayList<>();
        Object[] args = jp.getArgs();
        for (Object object : args) {
            list.add(object.toString());
        }
        return list.toString();
    }

    /**
     *	获取缓存的key
     *	key 定义在注解上,支持SPEL表达式
     */
    private String parseKey(String key,Method method,Object [] args){
        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String [] paraNameArr = u.getParameterNames(method);
        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法参数放入SPEL上下文中
        for(int i=0;i<paraNameArr.length;i++){
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(key).getValue(context, String.class);
    }

    private Map<String, Object> isChache(Class<?> cls, String methodName) {
        boolean isCache = false;
        Map<String, Object> map = new HashMap<>();
        Method[] methods = cls.getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName) && method.isAnnotationPresent(LzcCache.class)) {
                LzcCache lzcCache = method.getAnnotation(LzcCache.class); // 获取方法上的注解
                Class<?> methodReturnType = method.getReturnType(); // 获取方法的返回类型
                map.put("cacheName", lzcCache.cacheName());
                map.put("key", lzcCache.key());
                map.put("timeOut", lzcCache.timeOut());
                map.put("timeUnit", lzcCache.timeUnit());
                map.put("methodReturnType", methodReturnType);
                map.put("method", method);
                isCache = true;
                break;
            }
        }
        map.put("isCache", isCache);
        return map;
    }
}

(2)清除缓存切面

package com.lzc.aopcache.aspect;

import com.lzc.aopcache.annotation.LzcCacheEvict;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.StringRedisTemplate;
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 java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Aspect
@Component
public class LzcCacheEvictAspect {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Around("execution(* com.lzc.aopcache.*.*.*(..))")
    public Object lzcCacheAspect(ProceedingJoinPoint jp) throws Throwable {

        Class<?> cls = jp.getTarget().getClass();
        String methodName = jp.getSignature().getName();

        Map<String, Object> map = isChacheEvict(cls, methodName);
        boolean isCacheEvict = (boolean)map.get("isCacheEvict");

        if (isCacheEvict) {
            String cacheName = (String) map.get("cacheName"); // 缓存名字
            String key = (String) map.get("key"); // 自定义缓存key
            Method  method = (Method)map.get("method"); // 方法

            String realCacheName = "";
            // 判断cacheName是否为空,如果cacheName为空则使用默认的cacheName
            if(cacheName.equals("")) {
                realCacheName = cls.getName() + "." + methodName;
            } else {
                realCacheName = cacheName;
            }

            String realKey = "";
            // 判断key是否为空, 如果为空则使用默认的key
            if (key.equals("")) {
                realKey = realCacheName + "::" + defaultKeyGenerator(jp);
            } else {
                realKey = realCacheName + "::" + parseKey(key, method, jp.getArgs());
            }

            // 判断缓存中是否存在该key, 如果存在则直接从缓存中删除该数据
            if (stringRedisTemplate.hasKey(realKey)) {
                stringRedisTemplate.delete(realKey);
            }
        }
        return jp.proceed();
    }
    /**
     * 自定义生成key,使用方法中的参数作为key
     */
    private String defaultKeyGenerator(ProceedingJoinPoint jp) {
        // 获取所有参数的值
        List<String> list = new ArrayList<>();
        Object[] args = jp.getArgs();
        for (Object object : args) {
            list.add(object.toString());
        }
        return list.toString();
    }

    /**
     *	获取缓存的key
     *	key 定义在注解上,支持SPEL表达式
     */
    private String parseKey(String key,Method method,Object [] args){
        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String [] paraNameArr = u.getParameterNames(method);
        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法参数放入SPEL上下文中
        for(int i=0;i<paraNameArr.length;i++){
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(key).getValue(context, String.class);
    }

    private Map<String, Object> isChacheEvict(Class<?> cls, String methodName) {
        boolean isCacheEvict = false;
        Map<String, Object> map = new HashMap<>();
        Method[] methods = cls.getDeclaredMethods();

        for (Method method : methods) {
            if (method.getName().equals(methodName) && method.isAnnotationPresent(LzcCacheEvict.class)) {
                LzcCacheEvict lzcCacheEvict = method.getAnnotation(LzcCacheEvict.class); // 获取方法上的注解
                map.put("cacheName", lzcCacheEvict.cacheName());
                map.put("key", lzcCacheEvict.key());
                map.put("method", method);
                isCacheEvict = true;
                break;
            }
        }
        map.put("isCacheEvict", isCacheEvict);
        return map;
    }
}

四、使用

@GetMapping("/cache")
    @LzcCache(cacheName = "user",key = "#user.username", timeOut = 10, timeUnit = TimeUnit.MINUTES)
    public ResultVO cahce(User user) {
        System.out.println("进来了");
        return ResultVOUtil.success(user);
    }

    @GetMapping("/cache1")
    @LzcCacheEvict(cacheName = "user",key = "#user.username")
    public ResultVO cahce1(User user) {
        return ResultVOUtil.success("清除成功");
    }

代码地址:https://gitee.com/lzc_lzc/SpringBoot-learning/tree/master/aop-cache

猜你喜欢

转载自blog.csdn.net/lizc_lizc/article/details/81268748