SpringBoot结合Redis 自定义数据缓存

SpringBoot结合Redis 自定义数据缓存

缓存的主要目的是提升系统的性能、处理高并发情况,并减少对数据库的压力。通过将数据存储在内存中,当数据没有本质变化时,我们可以避免频繁地连接数据库进行查询。相反,我们可以首先尝试从缓存中读取数据,只有当缓存中未找到数据时,才会去数据库中查询。这样做可以显著减少数据库的读写操作,提高系统的性能和并发能力。

接下来我将写一个简单的缓存小栗子。。。
spring boot本身也有自带的缓存注解,但是我们现在来介绍的是通过Redis自定义的缓存

1. 依赖

我们需要引入redsi依赖和工具类依赖
版本自行添加

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <version><!-- 指定版本号 --></version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version><!-- 指定版本号 --></version>
    </dependency>
</dependencies>

2. 配置

在配置文件配置一下redsi

    #redis 配置
  redis:
    host: ip地址
    port: 6379
    password: 密码,有就设置,没有就删除,默认是没有
    connect-timeout: 10000  超时时间,自己看情况

3. 编写注解

/**
 * 自定义缓存处理注解
 *
 * @Author 君子慎独
 * @create 2021/01/25 16:41
 */
@Target(ElementType.METHOD) // 在方法上使用
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
@Documented
public @interface Cache {

    // 默认过期时间 5分钟
    long expire() default 5 * 60 * 1000;

    // 默认名称为空
    String name() default "";
}
/**
 * 处理缓存的切面类
 *
 * @Author 君子慎独
 * @create 2021/01/25 16:45
 */
@Aspect // AOP切面
@Component // 注册为Spring组件
@Slf4j // 日志注解
public class CacheAspect {

    // Redis模板类
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 切入点定义
     */
    @Pointcut("@annotation(com.qwt.test.common.cache.Cache)")
    public void cachePointcut() {
    }

    /**
     * 环绕通知方法,处理缓存逻辑
     *
     * @param joinPoint 切入点信息
     * @return 方法执行结果
     * @throws Throwable 可能抛出的异常
     */
    @Around("cachePointcut()")
    public Object cacheAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("进入缓存处理");
        try {
            // 获取方法签名
            Signature signature = joinPoint.getSignature();
            // 获取类名
            String className = joinPoint.getTarget().getClass().getSimpleName();
            // 获取方法名
            String methodName = signature.getName();
            // 获取方法参数
            Object[] args = joinPoint.getArgs();

            // 构建缓存Key
            String cacheKey = buildCacheKey(className, methodName, args);

            // 从缓存中获取结果
            String cachedResult = redisTemplate.opsForValue().get(cacheKey);

            if (StringUtils.isNotEmpty(cachedResult)) {
                log.info("命中缓存:{}.{}", className, methodName);
                return JSON.parseObject(cachedResult, Result.class);
            }

            // 执行方法并获取结果
            Object result = joinPoint.proceed();

            // 将结果存入缓存
            storeResultInCache(cacheKey, result);

            log.info("存入缓存:{}.{}", className, methodName);
            return result;
        } catch (Throwable throwable) {
            log.error("缓存处理发生异常:{}", throwable.getMessage());
            throwable.printStackTrace();
            throw throwable;
        }
    }

    /**
     * 构建缓存Key
     *
     * @param className  类名
     * @param methodName 方法名
     * @param args       方法参数
     * @return 缓存Key
     */
    private String buildCacheKey(String className, String methodName, Object[] args) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(className).append("::")
                .append(methodName).append("::");

        if (args != null && args.length > 0) {
            for (Object arg : args) {
                if (arg != null) {
                    keyBuilder.append(arg.toString());
                }
            }
        }

        return DigestUtils.md5Hex(keyBuilder.toString());
    }

    /**
     * 将方法执行结果存入缓存
     *
     * @param cacheKey 缓存Key
     * @param result   方法执行结果
     */
    private void storeResultInCache(String cacheKey, Object result) {
        long expireTime = getCacheExpireTime(result);
        String jsonResult = JSON.toJSONString(result);
        redisTemplate.opsForValue().set(cacheKey, jsonResult, Duration.ofMillis(expireTime));
    }

    /**
     * 获取缓存过期时间
     *
     * @param result 方法执行结果
     * @return 缓存过期时间
     */
    private long getCacheExpireTime(Object result) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Cache annotation = method.getAnnotation(Cache.class);
        if (annotation != null) {
            return annotation.expire();
        }
        // 默认过期时间 5分钟
        return 5 * 60 * 1000;
    }
}

注意:
redisKey :这个必须保证唯一 除开类名、方法名和缓存名称,保证这个key唯一的就是参数,所以在给没有参数的方法使用缓存时请慎重考虑,不然就会出现不同的用户之间获取到的数据一样和两个用户之间交换获取对方数据,这是非常致命的

4. 测试

这个注解实在controller层使用的,因为是测试就不要关心参数问题,所以我们这里测试就不给参数了

  @PostMapping("/test")
@Cache(name = "test", expire = 5000)
public Result test() throws InterruptedException {
    // 为了展现缓存效果,这里我们加个睡眠
    Thread.sleep(3000);
    return Result.success("缓存哈哈哈!");
}

使用postman查看
第一次:
在这里插入图片描述
第二次:在这里插入图片描述
缓存的效果就可以很明显的看出来了,那么这样我们非常简单的数据缓存就完成了

建议是在数据不经常修改或经常查询但是一段时间内不会改变的时候才使用,如果频繁更改的其实使用缓存的意义不大,当然也可以使用不过相对而言我这个就不适合了,想要更加好的代码来支持

参考:https://cloud.tencent.com/developer/article/1824707

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于SpringBoot定义Redis注解AOP的问题,我可以为您提供一些基本的介绍和示例代码。 Redis作为一种高性能的缓存数据存储解决方案,被广泛应用于各种应用程序中。在SpringBoot应用程序中,使用Redis通常需要编写大量的重复代码,比如获取Redis连接、执行Redis命令、释放Redis连接等。这些重复代码不仅增加了开发和维护的难度,还影响了应用程序的性能。而AOP作为一种切面编程的技术,可以很好地解决这些问题。 下面是一个简单的示例代码,演示如何通过自定义注解实现对Redis操作的AOP处理: 首先,定义一个自定义注解: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RedisCacheable { String key() default ""; long expire() default 0; } ``` 然后,在需要被拦截的方法上添加该注解: ```java @Component public class RedisService { @Autowired private RedisTemplate<String, String> redisTemplate; @RedisCacheable(key = "myKey", expire = 60) public String getValue() { return redisTemplate.opsForValue().get("myKey"); } } ``` 接下来,使用AspectJ的@Aspect注解定义一个切面类,并在该类中定义一个切点,用于匹配被@RedisCacheable注解的方法: ```java @Aspect @Component public class RedisAspect { @Autowired private RedisTemplate<String, String> redisTemplate; @Pointcut("@annotation(com.example.demo.annotation.RedisCacheable)") public void redisCacheablePointcut() {} @Around("redisCacheablePointcut()") public Object aroundRedisCacheable(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); RedisCacheable redisCacheable = method.getAnnotation(RedisCacheable.class); String key = redisCacheable.key(); long expire = redisCacheable.expire(); String value = redisTemplate.opsForValue().get(key); if (value != null) { return value; } Object result = joinPoint.proceed(); if (result != null) { redisTemplate.opsForValue().set(key, result.toString()); if (expire > 0) { redisTemplate.expire(key, expire, TimeUnit.SECONDS); } } return result; } } ``` 在该切面类中,使用@Around注解定义一个环绕通知,在该通知中,首先获取被拦截方法上的@RedisCacheable注解,然后根据注解中的key值从Redis中获取数据。如果Redis中已经存在该数据,则直接返回;否则,执行被拦截方法,并将结果存储到Redis缓存中。 最后,启动SpringBoot应用程序,调用RedisService的getValue方法,就可以看到输出结果: ```java // 第一次调用,从数据库中获取数据,并将数据存入Redis缓存中 getValue... // 第二次调用,直接从Redis中获取数据 getValue... ``` 以上就是一个简单的SpringBoot定义Redis注解AOP的示例。通过使用自定义注解和AOP技术,可以更加方便地实现对Redis缓存的操作,并提高应用程序的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值