打造redis缓存组件

使用热插拔aop+反射+redis自定义注解+spring EL表达式打造redis缓存组件,优雅重构缓存代码

redis配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableAspectJAutoProxy //V2  开启AOP自动代理
public class RedisConfig
{
    /**
     * @param lettuceConnectionFactory
     * @return
     *
     * redis序列化的工具配置类,下面这个请一定开启配置
     * 127.0.0.1:6379> keys *
     * 1) "ord:102"  序列化过
     * 2) "\xac\xed\x00\x05t\x00\aord:102"   野生,没有序列化过
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory)
    {
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        //设置key序列化方式string
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式json
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

自定义注解

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


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRedisCache     //@EnableAspectJAutoProxy //启AOP自动代理
{
    //约等于键的前缀prefix,
    String keyPrefix();

    //SpringEL表达式,解析占位符对应的匹配value值
    String matchValue();
}

AOP

import jakarta.annotation.Resource;
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.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
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.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Objects;


@Component
@Aspect
public class MyRedisCacheAspect
{
    @Resource
    private RedisTemplate redisTemplate;

    //配置织入点
    @Pointcut("@annotation(com.atguigu.interview2.annotations.MyRedisCache)")
    public void cachePointCut(){}

    @Around("cachePointCut()")
    public Object doCache(ProceedingJoinPoint joinPoint)
    {
        Object result = null;


        /**
         *     @MyRedisCache(keyPrefix = "user",matchValue = "#id")
         *     public User getUserById(Integer id)
         *     {
         *         return userMapper.selectByPrimaryKey(id);
         *     }
         */


        try
        {
            //1 获得重载后的方法名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            //2 确定方法名后获得该方法上面配置的注解标签MyRedisCache
            MyRedisCache myRedisCacheAnnotation = method.getAnnotation(MyRedisCache.class);

            //3 拿到了MyRedisCache这个注解标签,获得该注解上面配置的参数进行封装和调用
            String keyPrefix = myRedisCacheAnnotation.keyPrefix();
            String matchValueSpringEL = myRedisCacheAnnotation.matchValue();

            //4 SpringEL 解析器
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(matchValueSpringEL);//#id
            EvaluationContext context = new StandardEvaluationContext();

            //5 获得方法里面的形参个数
            Object[] args = joinPoint.getArgs();
            DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
            String[] parameterNames = discoverer.getParameterNames(method);
            for (int i = 0; i < parameterNames.length; i++)
            {
                System.out.println("获得方法里参数名和值: "+parameterNames[i] + "\t" + args[i].toString());
                context.setVariable(parameterNames[i], args[i].toString());
            }

            //6 通过上述,拼接redis的最终key形式
            String key = keyPrefix + ":" + expression.getValue(context).toString();
            System.out.println("------拼接redis的最终key形式: " + key);

            //7 先去redis里面查询看有没有
            result = redisTemplate.opsForValue().get(key);
            if (result != null)
            {
                System.out.println("------redis里面有,我直接返回结果不再打扰mysql: " + result);
                return result;
            }

            //8 redis里面没有,去找msyql查询或叫进行后续业务逻辑
            //-------aop精华部分,才去找findUserById方法干活
            //userMapper.selectByPrimaryKey(id);
            result = joinPoint.proceed();//主业务逻辑查询mysql,放行放行放行

            //9 mysql步骤结束,还需要把结果存入redis一次,缓存补偿
            if (result != null)
            {
                System.out.println("------redis里面无,还需要把结果存入redis一次,缓存补偿: " + result);
                redisTemplate.opsForValue().set(key, result);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return result;
    }
}

测试

    /**
     * 会将返回值存进redis里,key生成规则需要程序员用SpEL表达式自己指定,value就是程序从mysql查出并返回的user
     * redis的key 等于  keyPrefix:matchValue
     */
    @Override
    @MyRedisCache(keyPrefix = "user",matchValue = "#id")
    public User getUserById(Integer id)
    {
        return userMapper.selectByPrimaryKey(id);
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值