SpringBoot AOP控制Redis自动缓存和更新

导入redis的jar包

<!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>

相关jar包

	<dependency>
	    <groupId>org.projectlombok</groupId>
	    <artifactId>lombok</artifactId>
	</dependency>
	<!--JSON-->
    <dependency>
        <groupId>net.sf.json-lib</groupId>
        <artifactId>json-lib</artifactId>
        <version>2.4</version>
        <classifier>jdk15</classifier>
    </dependency>
    <dependency>
       <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.58</version>
    </dependency>

编写自定义缓存注解

/**
 * @Description: redis缓存注解 编写在需要缓存的类上
 **/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
}

编写切面类

package com.ys.edu.aop;

import com.ys.edu.utils.ResultUtils;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RedisAOP
 * @description: redis 切面缓存
 **/
@Aspect
@Slf4j
public class RedisAOP {

    private static final Logger logger = Logger.getLogger(RedisAOP.class);

    private static final Integer TIME_OUT = 30 ; //redis 存活时长  分钟
/**
     * 获取缓存验证
     */
    private static final Pattern GET_CACHE_PATTERN = Pattern.compile("^((get)|(query)|(select)|(list)).*$");

    /**
     * 更新缓存验证
     */
    private static final Pattern SET_CACHE_PATTERN = Pattern.compile("^((add)|(insert)|(save)|(batchInsert)|(batchUpdate)|(update)|(delete)).*$");


    @Resource
    private RedisTemplate redisTemplate;

    /**
     * @Title: queryCachePointcut
     * @Description: 定义切点为缓存注解
     * @return void
     **/
    @Pointcut("@within(com.ys.edu.annotation.RedisCache)")
    public void queryCachePointcut(){

    }

    @Around("queryCachePointcut()")
    public Object Interceptor(ProceedingJoinPoint joinPoint) throws Throwable{
        long beginTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //类路径名
        String classPathName = joinPoint.getTarget().getClass().getName();
        //类名
        String className = classPathName.substring(classPathName.lastIndexOf(".")+1);
        //获取方法名
        String methodName = signature.getMethod().getName();
        //参数名
        String[] strings = signature.getParameterNames();
        //参数值
        Object[] method_args = joinPoint.getArgs();
        String params = "";
        for (int i = 0; i < strings.length; i++) {
            params += strings[i] + "=" + method_args[i] + ",";
        }
        //拼接参数和参数值
        params = params.substring(0, params.length() - 1);
        String key = className+"_"+methodName+"_"+ params;
        // 如果是查询接口 则获取缓存
        if(GET_CACHE_PATTERN.matcher(methodName).matches()){
            Object data = getObject(beginTime,joinPoint,key);
            if(data != null){
                return JSONUtils.strToObject(JSONUtils.objectToStr(data),ResultBody.class);
            }
            return joinPoint.proceed();
            // 如果是更改或新增数据 则填充缓存
        }else if(SET_CACHE_PATTERN.matcher(methodName).matches()){
            Set<String> keys = redisTemplate.keys(className+"*");
            redisTemplate.delete(keys);
            log.warn("执行方法 : [ "+methodName+" ] :  清除 key 包含 [ "+className+" ] 的缓存数据");
            log.warn("AOP 清除缓存 >>>> end 耗时:" + (System.currentTimeMillis() - beginTime) + "ms.");
        }
        // 调用原始方法
        return joinPoint.proceed();
    }


    /**
     * @Title: getObject
     * @Description: 使用key获取数据  不存在则查询添加
     * @param beginTime : 切面开始时间
     * @param joinPoint : 切面对象
     * @param key : 获取redis数据的key值
     * @return java.lang.Object
     **/
    private Object getObject(long beginTime,ProceedingJoinPoint joinPoint,String key) throws Throwable {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        boolean hasKey = redisTemplate.hasKey(key);
        Object object = null;
        if(hasKey){
            // 缓存中获取到数据,直接返回。
            object = operations.get(key);
            log.warn("从缓存中获取到 key 为 ["+key+" ] : 的数据 >>>> " + object.toString());
            log.warn("AOP 获取缓存 >>>> end 耗时:" + (System.currentTimeMillis() - beginTime) + "ms.");
            return object;
        }
        if(object == null) {
            // 缓存中没有数据,调用原始方法查询数据库
            object = joinPoint.proceed();
            // 设置超时时间30分钟
            operations.set(key, object, TIME_OUT, TimeUnit.MINUTES);
            log.warn("向 Redis 添加 key 为 ["+key+" ] , 存活时长为 "+TIME_OUT+" min 的数据 >>>> " + object.toString());
            log.warn("AOP 添加缓存 >>>> end 耗时:" + (System.currentTimeMillis() - beginTime) + "ms.");
        }
        return object;
    }


    @Autowired(required = false)
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisSerializer stringSerializer = new StringRedisSerializer();//序列化为String
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//序列化为Json
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        this.redisTemplate = redisTemplate;
    }

}

在想要使用redis缓存的controller类上添加 @RedisCache 注解.

切面方法则会切以select/get/query 开头的查询方法,获取方法名和参数拼接为key,存到redis.

在执行add/insert/update 开头的方法时,则清空该类下的所有缓存.

方法返回值格式统一实体类:

package com.ys.edu.bean;

import java.io.Serializable;

/**
 * @ClassName ResultBody
 * @description:  RestFul API 方法返回值格式统一实体类
 **/
public class ResultBody<T> implements Serializable {

    private static final long serialVersionUID = 694858559908048578L;
    private Integer code;
    private String msg;
    private Integer count = 0;
    private T data;

    public ResultBody(){}

    public ResultBody(Integer code, String msg,Integer count,T data) {
        this.code = code;
        this.msg = msg;
        this.count = count;
        this.data = data;
    }

    public ResultBody(Integer code, String msg,T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    
    /**
     * @Title: success
     * @Description: 成功  (无参)  默认 code : " 0 "  msg : "请求成功" , count : 0 , data: null
     * @date 2018/11/29 10:28
     **/
    public ResultBody success(){
        return success((T) null);
    }

    /**
     * @Title: success
     * @Description:   成功   默认 code : " 0 "  msg : "请求成功"
     * @param count : 数据条数
     * @param data :  数据
     * @date 2018/11/29 11:46
     **/
    public ResultBody success(Integer count,T data){
        return new ResultBody(0,"请求成功!",count,data);
    }

    /**
     * @Title: success
     * @Description:  成功   默认 code : " 0 "
     * @param msg :  提示信息
     * @param count :  数据条数
     * @param data :   数据
     **/
    public ResultBody success(String msg,Integer count,T data){
        return new ResultBody(0,msg,count,data);
    }

    /**
     * @Title: success
     * @Description:  成功   默认 code : " 0 " , msg : "请求成功"
     * @param data :  数据
     **/
    public ResultBody success(T data){
        return new ResultBody(0,"请求成功!",data);
    }

    /**
     * @Title: success
     * @Description:  成功   默认 code : " 0 "
     * @param msg :  提示信息
     * @param data :  数据
     * @date 2018/11/29 11:47
     **/
    public ResultBody success(String msg,T data){
        return new ResultBody(0,msg,data);
    }

    /**
     * @Title: success
     * @Description:  成功   默认 code : " 0 "
     * @param code :  枚举类代码
     * @param data :  数据
     **/
    public ResultBody success(Code code,T data){
        return new ResultBody(code.getCode(),code.getMsg(),data);
    }

    /**
     * @Title: success
     * @Description:  成功   默认 code : " 0 "
     * @param code :  枚举类代码
     **/
    public ResultBody success(Code code){
        return new ResultBody(code.getCode(),code.getMsg(),null);
    }

    
    /**
     * @Title: error
     * @Description:  错误   默认 data : null
     * @param code : 错误代码
     * @param msg : 错误信息
     **/
    public ResultBody error(Integer code,String msg){
        return new ResultBody(code,msg,null);
    }

    /**
     * @Title: error
     * @Description:  错误   默认 data : null
     * @param code :  枚举类错误代码
     **/
    public ResultBody error(Code code){
        return new ResultBody(code.getCode(),code.getMsg(),null);
    }


    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

自定义提示枚举类:

package com.ys.edu.bean;

/**
 * @ClassName Code
 * @description: 自定义提示枚举类
 **/
public enum Code {

   
    /**
     * @Description:  请求状态码
     **/
    SUCCESS(0,"请求成功"),
    ERROR(-1,"请求错误");

    private Integer code;
    private String msg;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    Code(Integer code, String msg){
        this.code = code;
        this.msg = msg;
    }

}

返回结果工具类:

package com.ys.edu.utils;

import com.ys.edu.bean.Code;
import com.ys.edu.bean.ResultBody;
import com.ys.edu.entity.Page;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName ResultUtils
 * @description: 返回结果工具类
 **/
public class ResultUtils {

    /**
     * @Title: success
     * @Description: 无参成功返回   默认值  code : "0" , msg : "请求成功" , count : 0 , data : null
     **/
    public static ResultBody success(){
        return success((Object)null);
    }


    public static ResultBody success(Object object){
        return success(0,object);
    }


    /**
     * @Title: success
     * @Description:  有参成功返回   默认值  code : "0" , msg : "请求成功"
     * @param count :  数据条数
     * @param object : 数据
     **/
    public static ResultBody success(Integer count,Object object){
        return new ResultBody().success(count,object);
    }

    /**
     * @Title: success
     * @Description:  有参成功返回   默认值  code : "0"
     * @param msg : 提示信息
     * @param count :  数据条数
     * @param object :  数据
     **/
    public static ResultBody success(String msg,Integer count,Object object){
        return new ResultBody().success(msg,count,object);
    }

    /**
     * @Title: error
     * @Description: 有参成功返回     默认值  code : "0"
     * @param code :
     * @param object : 数据
     **/
    public static ResultBody success(Code code,Object object){
        return new ResultBody().success(code,object);
    }

    /**
     * @Title: error
     * @Description: 有参成功返回     默认值  code : "0" data : null
     * @param code : 枚举类代码
     **/
    public static ResultBody success(Code code){
        return new ResultBody().success(code);
    }

    /**
     * @Title: error
     * @Description: 错误返回格式     默认值 data : null
     * @param code : 错误代码
     **/
    public static ResultBody error(Integer code,String msg){
        return new ResultBody().error(code,msg);
    }
    
    
    /**
     * @Title: error
     * @Description: 错误返回格式     默认值 data : null
     * @param code : 枚举类错误代码
     **/
    public static ResultBody error(Code code){
        return new ResultBody().error(code);
    }

    
    /**
     * @Title: successByLimit
     * @Description: 分页返回数据格式
     * @param page : 查询的页数
     * @param limit : 查询的条数
     * @param totalNum : 数据总条数
     * @param curCount : 当前页条数
     * @param object : 查询结果数据
     **/
    /*public static ResultBody successByLimit(Integer page,Integer limit,Integer totalNum,Integer curCount,Object object){
        Map<String,Object> map = new HashMap<>();
        Page pageInfo = new Page();
        pageInfo.setPage(page);
        pageInfo.setLimit(limit);
        pageInfo.setTotalNum(totalNum);
        pageInfo.setTotalPages((totalNum + limit - 1)/limit);
        map.put("page",pageInfo);
        map.put("data",object);
        return success(curCount,map);
    }*/

}

JSONUtils 工具类

package com.common.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 *@Title JSONUtils.java
 *@description:  JSON工具类
 **/
public class JSONUtils {


    /**
     * @Title: strToObject
     * @Description: json字符串转Object对象
     * @param @param str
     * @param @param clazz
     * @return T
     */
    public static<T> T strToObject(String str,Class<T> clazz){
        ObjectMapper mapper = new ObjectMapper();
        T t = null;
        try {
            t = mapper.readValue(str, clazz);
            return t;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return t;
    }


    /**
     * @Title: objectToStr
     * @Description: Object对象转json字符串
     * @param @param t
     * @return String
     */
    public static<T> String objectToStr(T t){
        ObjectMapper mapper = new ObjectMapper();
        String str = null;
        try {
            str = mapper.writeValueAsString(t);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return str;
    }


    /**
     * @Title: strToList
     * @Description: json字符串转list对象集合
     * @param @param str
     * @param @param clazz
     * @return List<T>
     */
    public static <T> List<T> strToList(String str, Class<T> clazz) {
        JSONArray json = JSONArray.fromObject(str);
        JSONObject object = null;
        T t = null;
        List<T> list = new ArrayList<>();
        for (int i = 0; i < json.size(); i++) {
            object = JSONObject.fromObject(json.get(i));
            t = (T) JSONObject.toBean(object, clazz);
            list.add(t);
        }
        return list;
    }


    /**
     *  用于Fiegn 远程调用返回对象或集合获取值时  需使用此方法转化
     *  异常信息 java.util.LinkedHashMap cannot be cast to **
     * @param object
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T objectToVo(Object object,Class<T> clazz){
        JSONObject jsonObject=JSONObject.fromObject(object);
        T t = (T) JSONObject.toBean(jsonObject,clazz);
        return t;
    }

}
  • 4
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值