SpringBoot整合SpringCache使用缓存
文章目录
- 
- SpringBoot整合SpringCache使用缓存
 - 1.介绍
 - 2.SpringBoot整合
 - 
- 1.导入xml依赖
 - 2.配置yml
 - 3.使用@EnableCaching启用SpringCache
 - 4.@Cacheable
 - 5.@CachePut
 - 6.@CacheEvict
 - [7. @Caching](#7. @Caching)
 - 8.@CacheConfig
 
 - 3.其他属性配置
 - 
- 
- [1.`keyGenerator` 属性](#1.
keyGenerator属性) - [2. `cacheManager` 属性](#2. 
cacheManager属性) - [3.`cacheResolver` 属性](#3.
cacheResolver属性) - 4.CacheManagerCustomizer
 
 - [1.`keyGenerator` 属性](#1.
 
 - 
 
 
1.介绍
Spring Cache 提供了 Cache 和 CacheManager 接口来统一管理不同的缓存技术。Cache 是缓存的抽象,CacheManager 负责管理多个 Cache 实例。Spring Cache 支持多种缓存实现,包括:
- ConcurrentHashMap:默认的缓存实现,适用于简单的本地缓存。
 - Redis:基于 Redis 的分布式缓存,适用于高并发场景。
 - Ehcache:符合 JSR-107 标准的缓存实现,支持二级缓存。
 - Caffeine:基于 Java 8 的高性能缓存库,适用于需要高性能的场景。
 - JSR-107:支持 JSR-107 标准的缓存实现。
 
2.SpringBoot整合
本文基于springboot2.7版本测试
1.导入xml依赖
            
            
              xml
              
              
            
          
                  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        2.配置yml
            
            
              yml
              
              
            
          
          spring:
  cache:
    cache-names: user
    type: redis
    redis:
      #缓存前缀
      key-prefix: moshangshang_
      #是否启用缓存统计信息。
      enable-statistics: false
      #是否允许缓存 null 值。
      cache-null-values: true
      #写入 Redis 时是否使用 key prefix。
      use-key-prefix: true
  redis:
    port: 6379
    host: 127.0.0.1
    password: root
    lettuce:
      pool:
        max-active: 20 #连接池最大连接数(使用负值表示没有限制)
        max-idle: 8   #连接池中的最大空闲连接
        min-idle: 5   # 连接池中的最小空闲连接
    timeout: 6000   #连接超时时长(毫秒)
        如果cache-null-values:属性启用不能缓存null值,则缓存null时会抛出下方异常
            
            
              shell
              
              
            
          
          java.lang.IllegalArgumentException: Cache 'user' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.
        cache-names属性说明:
用于管理全的缓存key的全局配置,多个用逗号分割,比如 cache-names: user; use-key-prefix: false,则表示 @Cacheable(cacheNames = "user")之类的注解不会使用key-prefix指定的缓存前缀,未配置的缓存名称则采用默认全局配置
3.使用@EnableCaching启用SpringCache
            
            
              java
              
              
            
          
          @SpringBootApplication
@EnableCaching
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}
        4.@Cacheable
@Cacheable 用于标记方法或类,表示该方法的返回值可以被缓存。
当方法执行前,Spring 会检查缓存中是否存在相同 key 的缓存元素,如果存在则直接返回,否则执行方法并将结果存入缓存。
@Cacheable 的方法必须为 public:如果方法不是 public 的,Spring 无法通过代理来访问缓存。
- value 或 cacheNames:指定缓存的名称,可以是单个字符串或字符串数组。
 - key:指定缓存的键,可以使用 SpEL 表达式来定义。
 - condition:指定缓存的条件,只有当条件为 true 时,才会从缓存中获取结果。
 - unless:指定不将结果放入缓存的条件,即使结果存在,也不会被缓存。
 - sync:是否使用同步方式获取缓存,避免多个线程同时执行方法。
 - 在某些场景下,需要确保缓存和数据库的一致性,可以使用 @Cacheable 的 sync 属性来启用同步更新。且在多线程环境下确保只有一个线程执行查询。
 
            
            
              java
              
              
            
          
             /**
     * 根据id查询用户信息
     * 生成的key为moshangshang_user::38
     */
    @GetMapping("/query/{id}")
    @Cacheable(cacheNames = "user",key = "#id",unless = "#result == null")
    public User getById(@PathVariable Long id){
        return userService.getById(id);
    }
        5.@CachePut
@CachePut 用于标记方法,表示每次调用该方法时都会执行并存入缓存。
它总是会执行方法,并将结果添加到缓存中,不会检查缓存中是否存在相同 key 的缓存元素。
- value 或 cacheNames:指定缓存的名称,可以是单个字符串或字符串数组。
 - key:指定缓存的键,可以使用 SpEL 表达式来定义。
 - condition:指定缓存的条件,只有当条件为 true 时,才会从缓存中获取结果。
 - unless:指定不将结果放入缓存的条件,即使结果存在,也不会被缓存。
 
            
            
              java
              
              
            
          
              @PostMapping("/save")
    @CachePut( key = "#user.id")
    public User updateUser(User user) {
        userService.saveOrUpdate(user);
        return user;
    }
        6.@CacheEvict
@CacheEvict 用于标记方法,表示该方法执行时会清除缓存中的数据。
@CacheEvict 在方法执行期间抛出异常不会清空缓存:如果方法执行过程中抛出异常,@CacheEvict 的 allEntries 属性不会生效。
它可以用于删除缓存中的所有键值对,也可以用于清除特定的 key。
- value 或 cacheNames:指定缓存的名称,可以是单个字符串或字符串数组。
 - key:指定缓存的键,可以使用 SpEL 表达式来定义。
 - condition:指定缓存的条件,只有当条件为 true 时,才会从缓存中获取结果。
 - beforeInvocation:是否在方法执行前清除缓存,为 true 时在方法执行前清除缓存。
 - allEntries:是否清除所有缓存条目,为 true 时清除所有缓存。
 
            
            
              java
              
              
            
          
              /**
     * @CacheEvict 在方法执行期间抛出异常不会清空缓存:如果方法执行过程中抛出异常,@CacheEvict 的 allEntries 属性不会生效。
     * @CacheEvict 用于标记方法,表示该方法执行时会清除缓存中的数据。
     * 它可以用于删除缓存中的所有键值对,也可以用于清除特定的 key。
     * value 或 cacheNames:指定要清除的缓存名称。
     * key:指定要清除的缓存键,可以使用 SpEL 表达式来定义。
     * allEntries:是否清除所有缓存条目,为 true 时清除所有缓存。
     * beforeInvocation:是否在方法执行前清除缓存,为 true 时在方法执行前清除缓存。
     * condition:指定清除缓存的条件,只有当条件为 true 时,才会清除缓存。
     */
    @GetMapping("/delete/{id}")
    @CacheEvict(key = "#id", allEntries = false)
    public void deleteUser(@PathVariable Long id) {
        userService.removeById(id);
    }
        7. @Caching
@Caching 是一个组合注解,可以同时应用多个其他注解,表示该方法会同时执行 @Cacheable、@CachePut 和 @CacheEvict 的操作。
            
            
              java
              
              
            
          
              @GetMapping("/save/caching")
    @Caching(
            cacheable = @Cacheable( key = "#user.id"),
            put = @CachePut( key = "#user.id"),
            evict = @CacheEvict( key = "#user.id")
    )
    public User saveUser(User user) {
        userService.save(user);
        return user;
    }
        8.@CacheConfig
@CacheConfig 用于在类上设置公共的缓存配置,避免在每个方法上重复配置。
            
            
              java
              
              
            
          
          /**
 * @CacheConfig 用于在类上设置公共的缓存配置,避免在每个方法上重复配置。
 */
@RestController
@AllArgsConstructor
@RequestMapping("cache")
@CacheConfig(cacheNames = "user")
public class CacheController {
    private final IUserService userService;
 
    @GetMapping("/query/{id}")
    @Cacheable(key = "#id",unless = "#result == null")
    public User getById(@PathVariable Long id){
        return userService.getById(id);
    }
}
        3.其他属性配置
1.keyGenerator 属性
keyGenerator 属性用于指定默认的键生成器(Key Generator)。如果在方法上未显式指定 key 属性,则使用该属性值作为默认的键生成器。
1.配置生成器
            
            
              java
              
              
            
          
          @Configuration
public class CacheConfig {
    @Bean(name = "customKeyGenerator")
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> method.getName() + "[" + Arrays.asList(params) + "]";
    }
}
        等同于
            
            
              java
              
              
            
          
              @Bean(name = "customKeyGenerator")
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName() + "[" + Arrays.asList(params) + "]";
            }
        };
    }
        2.使用
生成的key为moshangshang_user::getById[[39]]
            
            
              java
              
              
            
          
              @Cacheable(unless = "#result == null",keyGenerator = "customKeyGenerator")
    public User getById(@PathVariable Long id){
        return userService.getById(id);
    }
        2. cacheManager 属性
cacheManager 属性用于指定当前使用的 CacheManager 实现类的名称。CacheManager 是 Spring 定义的一个接口,用于管理缓存(Cache)的创建和配置。Spring 提供了多种 CacheManager 的实现,例如 ConcurrentMapCacheManager、EhCacheCacheManager、CaffeineCacheManager 等。通过设置 cacheManager 属性,可以指定使用哪种缓存管理器来管理缓存。
创建自定义的缓存管理器
            
            
              java
              
              
            
          
          @Configuration
public class CacheConfig {
    
    @Bean
    public ConcurrentMapCacheManager mapCacheManager() {
        return new ConcurrentMapCacheManager("user-map","user");
    }
    
}
        cacheManager() 方法返回了一个 ConcurrentMapCacheManager 实例,并且指定了缓存名称。这个 CacheManager 将被用于管理名为 指定的缓存。
            
            
              java
              
              
            
          
              /**
     * 执行的是mapCacheManager的缓存管理器
     */
    @GetMapping("/manger/map/query/{id}")
    @Cacheable(cacheManager = "mapCacheManager")
    public User getRedisMangerById(@PathVariable Long id){
        return userService.getById(id);
    }
        3.cacheResolver 属性
cacheResolver 属性用于指定一个自定义的 CacheResolver 实现。默认情况下,Spring 使用 SimpleCacheResolver 来解析缓存操作。通过自定义 CacheResolver,可以实现更复杂的缓存逻辑,例如根据方法名动态选择缓存名称或缓存管理器。
cacheManager和cacheResolver是互斥的 :如果同时指定了cacheManager和cacheResolver,Spring 会抛出异常,因为CacheResolver的实现会忽略自定义的CacheManager。- 自定义 
CacheResolver的灵活性 :通过自定义CacheResolver,可以实现更灵活的缓存管理,例如根据方法名、参数或上下文动态选择缓存名称或缓存管理器 。 - Spring 4.1 及以上版本 :从 Spring 4.1 开始,
@Cacheable、@CachePut、@CacheEvict等注解的value属性不再是强制性的,因为CacheResolver可以提供缓存名称信息 
自定义缓存解析器
getById方法取user缓存名称下数据,其他取user-map下数据
            
            
              java
              
              
            
          
          public class CustomCacheResolver implements CacheResolver {
    
    private final ConcurrentMapCacheManager concurrentMapCacheManager;
    
    private final RedisCacheManager redisCacheManager;
    public CustomCacheResolver(ConcurrentMapCacheManager concurrentMapCacheManager, 
                               RedisCacheManager redisCacheManager) {
        this.concurrentMapCacheManager = concurrentMapCacheManager;
        this.redisCacheManager = redisCacheManager;
    }
    @Override
    public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
        Collection<Cache> caches = new ArrayList<>();
        if (context.getTarget().getClass() == CacheController.class) {
            if (context.getMethod().getName().equals("getRedisById")) {
                caches.add(redisCacheManager.getCache("user"));
            }else {
                caches.add(concurrentMapCacheManager.getCache("user-map"));
            }
        }
        return caches;
    }
}
        配置自定义缓存管理器并注册缓存解析器
配置了自定义的CacheManager会导致yml里面的相关配置失效(任何一个都会,比如指定map的缓存管理器,yml配redis,则redis的配置也不生效)
            
            
              java
              
              
            
          
              @Bean
    public ConcurrentMapCacheManager mapCacheManager() {
        return new ConcurrentMapCacheManager("user-map","user");
    }
    @Primary
    @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate) {
        return RedisCacheManager.builder(Objects.requireNonNull(redisTemplate.getConnectionFactory()))
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(10)) // 设置默认缓存过期时间为10分钟
                        .disableCachingNullValues()) // 禁用缓存空值
                .withInitialCacheConfigurations(initialCacheConfigurations()) // 设置特定缓存的配置
                .build();
    }
    private Map<String, RedisCacheConfiguration> initialCacheConfigurations() {
        Map<String, RedisCacheConfiguration> initialConfigurations = new HashMap<>();
        // 设置特定缓存的过期时间
        initialConfigurations.put("user", RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)) // 设置特定缓存的过期时间为1小时
                .disableCachingNullValues());
        return initialConfigurations;
    }
    @Bean
    public CacheResolver customCacheResolver(ConcurrentMapCacheManager concurrentMapCacheManager,
                                             RedisCacheManager redisCacheManager) {
        return new CustomCacheResolver(concurrentMapCacheManager,redisCacheManager);
    }
        测试
            
            
              java
              
              
            
          
            /**
     * 执行的是RedisCacheManager的缓存管理器
     */
    @GetMapping("/resolver/redis/query/{id}")
    @Cacheable(unless = "#result == null",cacheResolver = "customCacheResolver")
    public User getRedisById(@PathVariable Long id){
        return userService.getById(id);
    }
    /**
     * 执行的是ConcurrentMapCacheManager的缓存管理器
     */
    @GetMapping("/resolver/map/query/{id}")
    @Cacheable(cacheNames = "user-map",unless = "#result == null",cacheResolver = "customCacheResolver")
    public User getMapCacheById(@PathVariable Long id){
        return userService.getById(id);
    }
        4.CacheManagerCustomizer
CacheManagerCustomizer 是一个用于在缓存管理器初始化之前对其进行自定义配置的接口。通过实现该接口的 Bean,可以对缓存管理器进行定制,例如设置缓存名称、是否允许缓存空值、设置缓存过期时间等。
- 自定义缓存配置 :
CacheManagerCustomizer允许在缓存管理器初始化之前对缓存进行配置,例如设置缓存名称、过期时间、序列化方式等。这使得开发者可以针对不同的缓存需求进行灵活配置。 - 覆盖默认配置 :如果使用了 
CacheManagerCustomizer,那么application.yml或application.properties中的缓存配置将不会生效,因为CacheManagerCustomizer会覆盖默认的配置 。 - 支持多种缓存类型 :
CacheManagerCustomizer不仅适用于ConcurrentMapCacheManager,还可以用于其他类型的缓存管理器,如RedisCacheManager、CaffeineCacheManager等。通过实现CacheManagerCustomizer接口,可以对不同类型的缓存管理器进行统一的配置 。 - 提供回调机制 :
CacheManagerCustomizer提供了一个customize方法,该方法会在缓存管理器初始化之前被调用,允许开发者对缓存管理器进行定制。例如,可以设置缓存名称、允许或禁止缓存空值等 。 
            
            
              java
              
              
            
          
          @Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
    return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
        @Override
        public void customize(ConcurrentMapCacheManager cacheManager) {
            cacheManager.setCacheNames(Arrays.asList("user"));
            cacheManager.setAllowNullValues(false); // 禁用缓存空值
        }
    };
}
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
        return (builder) -> {
            builder.cacheDefaults(
                    RedisCacheConfiguration.defaultCacheConfig()
                            .entryTtl(Duration.ofMinutes(10)) // 默认过期时间为 10 分钟
                            .disableCachingNullValues()); // 禁用缓存空值
        };
    }
        如果配置了自定义的缓存管理器(redisCacheManager),则CacheManagerCustomizer将不生效