Bootstrap

Spring Boot 中使用缓存(Cache)

Spring Boot 中使用缓存(Cache)

缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取。在 Spring Boot 中,使用缓存非常简单,主要依赖于 Spring Cache 提供的注解支持。本文将介绍如何在 Spring Boot 中使用缓存,并详细解释常用的三个注解:@Cacheable@CachePut@CacheEvict

1. 添加 Maven 依赖

使用 Redis 作为缓存提供者,在 Spring Boot 项目的 pom.xml 文件中添加如下相关的依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2. 在配置文件中加入相关配置

使用默认 lettuce 作为客户端,配置如下:

spring:
  redis:
    host: x.x.x.x
    port: 6379
    password: # Redis 服务器密码。
    database: 0 # Redis 数据库号,默认为 0。
    timeout: 1000 # Redis 连接超时时间,单位:毫秒。
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。
        max-idle: 8 # 默认连接数最小空闲的连接数,默认为 8 。使用负数表示没有限制。
        min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。
        max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。
  cache:
    type: redis
    redis:
      key-prefix: demo
      time-to-live: 6000

3. 创建 Redis 配置类

@EnableCaching // 启用 Spring Boot 的缓存功能
@Configuration
public class CacheConfig {

    private static final String COLON = ":";

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        // 创建默认的 RedisCacheConfiguration
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        // 获取 Redis 相关的配置属性
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();

        // 配置缓存键的前缀
        config = config.computePrefixWith(cacheName -> {
            String keyPrefix = redisProperties.getKeyPrefix();
            if (StringUtils.hasText(keyPrefix)) {
                // 确保前缀以 COLON 结尾
                keyPrefix = keyPrefix.lastIndexOf(COLON) == -1 ? keyPrefix + COLON : keyPrefix;
                return keyPrefix + cacheName + COLON; // 生成带前缀的缓存键
            }
            return cacheName + COLON; // 无前缀时,直接使用缓存名称
        });

        // 设置键的序列化方式
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(
            RedisSerializer.string()));

        // 设置值的序列化方式,使用自定义的对象序列化器
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
            buildRedisObjectSerializer()));

        // 配置缓存项的生存时间
        if (Objects.nonNull(redisProperties.getTimeToLive())) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        // 配置是否缓存空值
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        // 配置是否使用键前缀
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        // 返回配置好的 RedisCacheConfiguration
        return config; 
    }

    /**
     * 构建自定义的 Redis 对象序列化器。
     *
     * @return
     */
    private RedisSerializer<Object> buildRedisObjectSerializer() {
        ObjectMapper om = new ObjectMapper();
        // 设置所有属性的可见性
        om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
        // 启用默认的多态类型支持
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), DefaultTyping.NON_FINAL);
        // 禁用将日期键序列化为时间戳
        om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
        // 注册 Java 8 时间模块
        om.registerModules(new JavaTimeModule());

        return new GenericJackson2JsonRedisSerializer(om);
    }
}

4. 常用注解

@Cacheable

作用

@Cacheable 注解添加在方法上,注解用于缓存方法的返回值。当方法被调用时,会首先检查缓存中是否已有结果。如果存在,则直接返回缓存中的结果;如果不存在,则执行方法,并将结果存入缓存。

参数含义
  • cacheNames:缓存名,可以填写一个或多个缓存名。
  • value: 和 cacheNames 属性相同,是它的别名。
  • key: 用于指定缓存中条目的键。可以使用 SpEL 表达式来生成键。
  • condition: 可选项,只有在条件满足时才会缓存结果。使用 SpEL 表达式。
  • unless: 可选项,只有在条件不满足时才会缓存结果。使用 SpEL 表达式。
区别

@Cacheable 适用于只读操作,适合在数据未发生变化时使用,如查询操作。

@CachePut

作用

@CachePut 注解添加在方法上,注解用于将方法的返回值更新到缓存中。每次调用方法时,都会执行方法,并将返回值放入缓存,适用于更新或添加数据时。

参数含义
  • cacheNames:缓存名,可以填写一个或多个缓存名。
  • value: 和 cacheNames 属性相同,是它的别名。
  • key: 用于指定缓存中条目的键,可以使用 SpEL 表达式。
  • condition: 可选项,只有在条件满足时才会缓存结果。使用 SpEL 表达式。
  • unless: 可选项,只有在条件不满足时才会缓存结果。使用 SpEL 表达式。
区别

@CachePut 适用于需要强制更新缓存的场景,适合用于修改操作。每次调用都会执行方法,无论缓存中是否存在数据。

@CacheEvict

作用

@CacheEvict 注解添加在方法上,注解用于从缓存中移除指定的条目。适用于数据被删除或更新时,确保缓存与数据库的同步。

参数含义
  • cacheNames:缓存名,可以填写一个或多个缓存名。
  • value: 和 cacheNames 属性相同,是它的别名。
  • key: 用于指定缓存中条目的键,可以使用 SpEL 表达式。
  • condition: 可选项,只有在条件满足时才会缓存结果。使用 SpEL 表达式。
  • allEntries: 可选项,如果设置为 true,将移除缓存中所有条目。使用 SpEL 表达式。
区别

@CacheEvict 主要用于删除缓存条目,适合在数据更新或删除后使用,以防止缓存中的数据过时。

小结

  • @Cacheable: 用于读取数据,缓存方法的返回值。
  • @CachePut: 用于更新数据,每次调用都会执行方法并更新缓存。
  • @CacheEvict: 用于删除缓存中的条目,确保缓存数据的一致性。

5. 使用示例

常用的三个注解:@Cacheable@CachePut@CacheEvict。如下是使用示例:

@Service
public class UserService {

  	@Cacheable(cacheNames = "users")
  	public List<User> findUserById() {
			// 查询数据库
			return userMapper.selectList();
  	}
  
    @CachePut(cacheNames = "users", key = "#user.userId")
    public User updateUser(User user) {
        // 更新用户数据
        return user; // 返回更新后的用户
    }
  
    @CacheEvict(cacheNames = "users", key = "#userId")
    public void deleteUser(Long userId) {
        // 删除用户逻辑
    }
}

5. 结论

本文介绍了在 Spring Boot 中使用缓存的基本方法,重点讲解了 @Cacheable@CachePut@CacheEvict 注解的使用。通过合理地使用这些注解,可以显著提高应用程序的性能和响应速度。随着业务的复杂化,还可以根据需求选择合适的缓存提供者来满足性能和功能的需求。

;