自定义注解,使用redis缓存

当使用缓存的时候,需要先查询缓存是否有数据,如果有就直接返回;如果没有,就需要去查询数据库,再写进缓存中(可根据结果判断是否需要写入)。每次查询都需要做这个步骤,难免会有点繁琐,可以使用自定义注解,加上sprignAOP实现写入缓存功能。

一、前置准备

二、代码实现

1.自定义注解

package com.frame.util.annotion;


import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: towards
 * @CreateTime: 2022-07-17  14:36
 * @Description: TODO
 * @Version: 1.0
 */
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCache {

    // 缓存键
    String key() default "";

//    缓存键生成策略
    String keyGenerator() default "";

//    缓存时长
    long timeout() default 1;

//    缓存时间单位
    TimeUnit timeUnit() default TimeUnit.HOURS;

//    缓存条件判断
    String condition() default "";

    boolean sync() default false;

    String md5() default "";

    String id() default "";


}

2.切面

package com.frame.util.aspect;

import com.frame.util.annotion.RedisCache;
import com.frame.util.entity.vo.ResultVo;
import com.frame.util.service.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author: towards
 * @CreateTime: 2022-07-17  17:12
 * @Description: TODO
 * @Version: 1.0
 */
@Component
@Slf4j
@Aspect
public class CacheAspect {

    @Autowired
    RedisService redisService;

    @Pointcut("@annotation(com.frame.util.annotion.RedisCache)")
    public void pc(){};

   // @Before("pc()")
   // public <T> ResultVo before(JoinPoint joinPoint){
   //     Method method = getMethod(joinPoint);
   //     RedisCache annotation = method.getAnnotation(RedisCache.class);
   //     String redisKey = generatorRedisKey(annotation,joinPoint);
   //     T value = redisService.getKey(redisKey);
   //     return ResultVo.success(value);// 前置通知。return依然会执行切点的方法逻辑
   // }
   //
   //  @AfterReturning(value = "@annotation(com.frame.util.annotion.RedisCache)",returning = "result")
   //  public void afterReturn(JoinPoint joinPoint,ResultVo result){
   //      Method method = getMethod(joinPoint);
   //      RedisCache annotation = method.getAnnotation(RedisCache.class);
   //      long timeout = annotation.timeout();
   //      TimeUnit timeUnit = annotation.timeUnit();
   //      if (result.getCode() == "00000" && ObjectUtils.isNotEmpty(result.getData())){
   //          // 存入redis
   //          String redisKey = generatorRedisKey(annotation,joinPoint);
   //          redisService.setKey(redisKey,result,timeout,timeUnit);
   //      }
   //  }

    // @Around(value = "@annotation(com.frame.util.annotion.RedisCache)")
    @Around("pc()")
    public <T> ResultVo around(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = getMethod(joinPoint);
        RedisCache annotation = method.getAnnotation(RedisCache.class);
        // 查询redis是否有缓存
        String redisKey = generatorRedisKey(annotation,joinPoint);
        T value = redisService.getKey(redisKey);
        // 缓存中存在,直接返回
        if (ObjectUtils.isNotEmpty(value)){
            return ResultVo.success(value);
        }
        // 缓存中不存在,执行方法
        ResultVo resultVo = (ResultVo) joinPoint.proceed();
        // 将结果存入redis
        long timeout = annotation.timeout();
        TimeUnit timeUnit = annotation.timeUnit();
        if (resultVo.getCode().equals("00000") && ObjectUtils.isNotEmpty(resultVo.getData())){
            // 存入redis
            redisService.setKey(redisKey,resultVo,timeout,timeUnit);
        }
        return resultVo;
    }

    public Method getMethod(JoinPoint joinPoint){
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        return method;
    }

    /**
     * @description: 生成redis键
     * @author: towards
     * @date:  18:55
    * @param: id
    * @param: md5
    * @return: java.lang.String
     **/
    public String redisKey(String id,Map md5){
        return "PHONE_"+id+"_"+md5.hashCode();
    }

    /**
     * @description: 根据注解获取参数并生成redis键
     * @author: towards
     * @date: 2022/7/17 20:02
     * @param: annotation
     * @param: joinPoint
     * @return: java.lang.String
     **/
    public String generatorRedisKey(RedisCache annotation,JoinPoint joinPoint){
        // 获取被注解方法的形参
        String md5 = annotation.md5();
        Map md5Key = (Map) generateKeyBySpEL(md5, joinPoint);
        String id = annotation.id();
        String idKey = String.valueOf(generateKeyBySpEL(id, joinPoint));
        String redisKey = redisKey(idKey, md5Key);
        return redisKey;
    }

    /**
     * @description: 动态获取被注解的方法上的参数,使用  #param   格式获取
     * @author: towards
     * @date:  20:11
     * @param: spELString
     * @param: joinPoint
     * @return: java.lang.String
     **/
    public Object generateKeyBySpEL(String spELString, JoinPoint joinPoint){
        if(StringUtils.isBlank(spELString)){
            return null;
        }
        // 通过joinPoint获取被注解方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        //创建解析器
        SpelExpressionParser parser = new SpelExpressionParser();
        //获取表达式
        Expression expression = parser.parseExpression(spELString);
        //设置解析上下文(有哪些占位符,以及每种占位符的值)
        EvaluationContext context = new StandardEvaluationContext();
        //获取参数值
        Object[] args = joinPoint.getArgs();
        //获取运行时参数的名称
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i],args[i]);
        }
        //解析,获取替换后的结果
        Object result = expression.getValue(context);
        return result;
    }

}

  • 注意这里需要使用环绕通知,前置通知执行完之后,会继续执行方法逻辑,可以在环绕通知执行方法逻辑前return。
  • 动态获取参数值可以使用上面的generateKeyBySpEL方法

3.使用该注解

@RedisCache(timeout = 100,timeUnit = TimeUnit.SECONDS,md5 = "#mapParam",id = "#id")
    // @CacheInterCeptor(md5 = "#mapParam",id = "#id")
    public <T> ResultVo getPhone(Integer id, Map<String, Object> mapParam) throws Exception {
        T value = redisService.getKey(keyGenorate(id));
        TbPhone tbPhone = tbPhoneDao.queryById(id);
        if (!ObjectUtils.isEmpty(tbPhone)){
            log.info("query from mysql ,and save to redis");
            return ResultVo.success(tbPhone);
        }
        // 缓存和数据库都没有
        return ResultVo.failure(ResultCode.CRUD_ERROR);
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值