打造分布式缓存组件【场景】

本文将采用AOP + 反射 + Redis自定义缓存标签,重构缓存代码,打造基础架构分布式缓存组件

配置

需要在Redis配置类中开启AOP自动代理,即通过==@EnableAspectJAutoProxy ==注解实现该功能

java 复制代码
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * Redis配置类
 *
 */
@Configuration
@EnableCaching
@EnableAspectJAutoProxy // 开启AOP自动代理
public class RedisConfig {
    @Bean
    @Primary
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //String的序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 使用GenericJackson2JsonRedisSerializer 替换默认序列化(默认采用的是JDK序列化)
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();

        //序列号key value
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

自定义注解

java 复制代码
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 BaizeRedisCache {

    // redis的key前缀
    String keyPrefix();

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

}

定义Redis缓存组件切面类

java 复制代码
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
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.reflect.Method;

/**
 * @Description:redis缓存组件切面类
 * @Author: smart
 * @Date: 2024/8/7 8:40
 */
@Aspect
@Component
public class BaizeRedisCacheAspect {

    @Resource
    private RedisTemplate redisTemplate;

    // 配置织入点
    @Pointcut("@annotation(com.atguigu.daijia.common.config.redis.BaizeRedisCache)")
    public void cachePoint(){}

    @Around("cachePoint()")
    public Object doCache(ProceedingJoinPoint joinPoint){
        // TODO
        Object result = null;

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

            // 2、确定方法名上配置的注解标签
            BaizeRedisCache baizeRedisCache = method.getAnnotation(BaizeRedisCache.class);

            // 3、获得该注解标签上配置的参数进行封装和调用
            String keyPrefix = baizeRedisCache.keyPrefix();
            String matchValueSpringEL = baizeRedisCache.matchValue();

            // 4、SpringEL解析器
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(matchValueSpringEL);

            StandardEvaluationContext 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){
                // 若redis存在则直接返回结果,无需到数据库中查询
                return result;
            }

            // 8、redis中没有则去数据库中查询
            result = joinPoint.proceed();

            // 9、主业务逻辑查询完之后,将数据存入redis
            if (result != null){
                redisTemplate.opsForValue().set(key, result);
            }

        }catch (Throwable throwable){
            throwable.printStackTrace();
        }

        return result;
    }
}

应用

相关推荐
Bingo_BIG4 天前
Java Spring框架:单表的查询、新增、修改、删除、导入、导出
java·spring·前后端
全栈弄潮儿²⁰²⁴4 天前
AI Agent 开发实战(30):限流、缓存与成本控制
人工智能·gpt·缓存·agent·限流·agi·成本控制
Thomas.Sir4 天前
第21课:PyTorch|GPU多卡训练与分布式训练基础【让多卡并行成为你的加速引擎】
人工智能·pytorch·分布式
paopaokaka_luck4 天前
智慧社区综合服务小程序(人脸识别、AI问答、Echarts图形化分析)
前端·javascript·spring boot·spring·数据分析·echarts
牧瀬クリスだ4 天前
YAML配置详解:SpringBoot实战指南
java·spring·yml
需要8264 天前
JVM 内存区域与对象创建:一次 GC 从哪来
java·jvm·spring boot·spring·servlet·tomcat
Cicada1284 天前
库存消息消费的正确性设计——从幂等窗口到批量流水线
分布式·系统架构
paopaokaka_luck4 天前
考研政治刷题小程序(AI推荐题目、ECharts数据分析、章节练习与专项训练、模拟考试、错题本与收藏夹、学习打卡与目标管理、题库和组卷维护)
前端·javascript·spring boot·spring·数据分析·echarts
步行cgn4 天前
Spring 基于 XML 的自动装配:byType 详解
java·后端·spring
边境悍匪4 天前
蜗牛学苑 Java 智能体学习 Day46|贯穿项目 2 思维导图复盘
java·开发语言·vue.js·学习·spring