SpringBoot2.x使用缓存注解操作Redis

为了进一步简化 Redis 的使用, Spring还提供了缓存注解,使用这些注解可以有效简化编程过程。

缓存管理器和缓存的启用

Spring 在使用缓存注解前,需要配置缓存管理器,缓存管理器将提供一些重要的信息,如缓存类型、超时时间等。 Spring 可以支持多种缓存的使用,因此它存在多种缓存处理器,并提供了缓存处理器的接口 CacheManager 和与之相关的类。
在这里插入图片描述
从图中可以看到, Spring 可以支持多种缓存管理机制,而整合 Redis,主要就是以使用类 RedisCacheManager 为主。 在 Spring Boot的 starter 机制中,允许我们通过配置文件生成缓存管理器。

缓存管理器配置

使用配置文件生成 Redis 缓存管理器

在项目的配置文件中加入如下配置

#配置redis缓存管理器
#缓存类型,在默认的情况下,spring会自动根据上下文检索
spring.cache.type=redis
spring.cache.cache-names=redisCache,hashCache

配置参数解析

  • spring.cache.type
    spring.cache.type 配置的是缓存类型,这里配置为 Redis, Spring Boot 就会自动生成 RedisCacheManager 对象。
  • spring.cache.cache-names
    spring.cache.cache-names 配置的是缓存名称,多个名称可以使用逗号分隔,以便于缓存注解的引用。

最后为了使用缓存管理器,需要在 Spring Boot 的配置文件中加入驱动缓存的注解@EnableCaching,这样就可以驱动 Spring 缓存机制工作了。

启用缓存机制
@SpringBootApplication
@EnableCaching
public class RedisCacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisCacheApplication.class, args);
    }
}
常用的缓存注解
@Cacheable

使用了此注解的方法会将返回值放到缓存数据库中

参数解析
  • value
    指定一个配置文件中的缓存策略,如果没有指定value,则使用默认的缓存策略。
  • key
    给存储的值起个名称,在查询时如果缓存中有名称相同的,那么则直接从缓存中将数据返回。
@CacheEvict

清除缓存

参数解析
  • value
    清除指定的缓存策略的所有对象,如果没有指定value,那么使用默认的缓存策略。
Spring 缓存注解的使用
@Override
@Cacheable(value = "redisCache", key = "'redis_user_'+#userId")
public User findUserById(Long userId) {
    log.info("findUserById入参=" + userId);
    Optional<User> byId = this.userDao.findById(userId);
    return byId.get();
}

在这里插入图片描述

Key的生成策略

通过观察上图可以发现,采用此种配置,key的生成规则是#{cacheName}::#{key}。

更新数据库中的数据
@Override
@Transactional
@CachePut(value = "redisCache", key = "'redis_user_'+#result.userId"
        , condition = "#result!='null'")
public User updateUser(User user) {
    log.info("updateUser入参=" + JSON.toJSONString(user));
    User userById = this.findUserById(user.getUserId());
    if (Objects.isNull(userById)){
        System.out.println("数据错误");
    }
    User save = this.userDao.save(user);
    return save;
}

在调用这个方法的时候,可以看到执行了查询数据库的操作,所以并没有查缓存!
在这里插入图片描述
问题分析
更新数据,需要慎重一些。一般我们不能轻易地相信缓存,因为缓存存在脏读的可能性, 这是需要注意的,在需要更新数据时我们往往考虑先从数据库查询出最新数据,而后再进行操作。因此,这里使用了 findUserById 方法,这里会存在一个误区,findUserById 方法存在注解@Cacheable,所以会从缓存中读取数据,从而拿着缓存中的数据去更新数据库的数据,这是一个非常危险的行为!然而 通过上面的测试却发现查的是数据库的数据,也就是说@Cacheable 这个注解失效了!即使用 updateUser 方法调用 findUserById 方法,但是并不存在读取缓存的可能,它每次都会执行 SQL 从数据库中去查询数据,而且这便是缓存注解自调用失效的问题。

缓存注解自调用失效问题

和数据库事务的自调用失效原理一样,因为 Spring 的缓存机制也是基于 Spring AOP 实现的,而在 Spring 中 AOP 是通过动态代理技术来实现的,这里的 updateUser方法调用 findUserById 方法是类内部的自调用, 并不存在代理对象的调用 ,这样便不会出现 AOP,也就不会使用到标注在 findUserById 上的缓存注解,更不会去获取其缓存的值了,这是需要注意的地方。也是我们在实际的工作和学习中我们需要注意这些问题。
AOP自调用失效演示案例:传送门【该项目的AOP子模块】

自定义缓存管理器

在 Spring Boot 中,如果采取上一种配置,则 RedisCacheManage中会采用永不超时的机制,如果我们并不希望采用 Spring Boot 机制带来的键命名方式,也不希望缓存永不超时,这时我们可以自定义缓存管理器。毕竟永不超时的机制不利于数据的及时更新

重置 Redis 缓存管理器

修改配置文件的内容

#是否允许redis缓存空值
spring.cache.redis.cache-null-values=true
#redis的键前缀
spring.cache.redis.key-prefix=
#缓存超时时间戳,配置为0则不设置超时时间
spring.cache.redis.time-to-live=6000ms
#是否启用redis的键前缀
spring.cache.redis.use-key-prefix=false

在这里插入图片描述
可以观察到,key的规则已经发生改变。
有时候,在重置 Redis 缓存管理器时可能存在比较多的配置, 也可以不采用 Spring Boot 自动配置的缓存管理器, 而是使用自定义的缓存管理器,这也是没有问题的。首先需要删除关于 Redis 缓存管理器的配置。然后给 IoC 容器增加缓存管理器。

自定义缓存管理器
  • 方式一:
@Autowired
private RedisConnectionFactory redisConnectionFactory;

/**
 * 自定义Redis缓存管理器
 *
 * @return
 */
@Bean(name = "redisCacheManager")
public RedisCacheManager iniRedisCacheManager() {
//        redis加锁的写入器
    RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory);
//        启用redis缓存的默认配置
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//        设置JDK序列化器
    config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()));
//        禁用前缀
    config = config.disableKeyPrefix();
//        设置超时时间10分钟
    config.entryTtl(Duration.ofMinutes(10));
    RedisCacheManager redisCacheManager = new RedisCacheManager(writer, config);
    return redisCacheManager;
}
  • 方式二
@EnableCaching
@SpringBootConfiguration
@EnableConfigurationProperties(CacheProperties.class)
public class MyCacheManager {

	
    /**
     * 修改序列化方式
     * org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration.createConfiguration
     * 从源码中拿过来的,修改序列化即可
     * 
     * @param cacheProperties 配置文件
     * @return redis 缓存配置
     */
    @Bean
    public RedisCacheConfiguration createConfiguration(CacheProperties cacheProperties) {
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

//        序列化方式
        RedisSerializationContext.SerializationPair<String> stringSerializationPair =
                RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());

        config = config.serializeKeysWith(stringSerializationPair);
        config = config.serializeValuesWith(stringSerializationPair);

        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }

}

这里首先注入了 RedisConnectionFactory 对象,该对象是由 Spring Boot 自动生成的。在创建 Redis 缓存管理器对象 RedisCacheManager 的时候,首先创建了带锁的 RedisCacheWriter 对象, 然后使用 RedisCacheConfiguration 对其属性进行配置,这里设置了禁用前缀,并且超时时间为 10 min:最后就通过 RedisCacheWriter 对象和 RedisCacheConfiguration 对象去构建 RedisCacheManager 对象了,这样 就完成了 Redis 缓存管理器的自定义。
项目案例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值