SpringBoot缓存详解

351 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情

一,介绍

SpringBoot 的缓存可以使用本地缓存,也可以整合第三方缓存,比如:redis、guava、ehcahe、jcache等等。 接下来主要介绍SpringBoot的缓存整合Redis的使用。

注:下面介绍的方式主要是通过注解来进行缓存的使用,当然,你还可以使用RedisTemplate来操作Reids缓存在pom.xml中添加以下依赖

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

二,本地缓存

2.1概念

Java Caching定义了五个核心接口,分别是CachingProvider、CachingManager、Cache、Entry、Expiry

  • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
  • CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
  • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
  • Entry是一个存储在Cache中的key-value对。
  • Expiry每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

2.2几个重要的缓存注解

@EnableCaching 开启基于注解的缓存; 可以加在主运行类上,也可以加在配置类上;

@EnableCaching
@SpringBootApplication
public class DelespringApplication {
public static void main(String[] args) {
    SpringApplication.run(DelespringApplication.class, args);
    }
}
@EnableCaching
@Configuration
public class RedisConfig{
    
}

@Cacheable 主要针对方法配置,能够根据方法的请求参数对其进行缓存

@Cacheable(value = "cach1")
@RequestMapping("list")
public List<Student> fun(Integer id,Integer age) {
    List<Student> list = studentMapper.selectList(null);
    return list;
}

常用参数:

value/cacheNames 缓存的名称,也是缓存的命名空间

key 缓存的键,可以为空,如果指定要按照 SpEL 表达式编写; 默认的key

  • 单参数:cacheNames::arg
  • 无参数: cacheNames::SimpleKey [], 后面使用 SimpleKey []来补齐
  • 多参数: cacheNames::SimpleKey [arg1, arg2…]
  • 非基础对象:cacheNames::obj.toString()

使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如: @Cacheable(value="users", key="#id") @Cacheable(value="users", key="#p0") keyGenerator 缓存键的生成器

@Configuration
public class MyConfig extends CachingConfigurerSupport {
    @Autowired
    private RedisConnectionFactory factory;
​
    @Bean
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                String name = method.getName();
                return name;
            }
        };
    }
}
@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;
​
    @Cacheable(value = "cach1",keyGenerator = "keyGenerator")
    @RequestMapping("list")
    public List<Student> fun(Integer id,Integer age) {
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}
  • cacheManager 缓存管理器,用法同上。
  • condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”)
  • unless 否定缓存。当条件结果为TRUE时,就不会缓存。@Cacheable(value=”testcache”,unless=”#userName.length()>2”)

@CachePut 保证方法被调用,又希望结果被缓存。与@Cacheable区别在于是否每次都调用方法,常用于更新,常用参数和 @Cacheable 大致一样

@CacheEvict 清空缓存 常用参数:

  • allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如:@CachEvict(value=”testcache”,allEntries=true)
  • beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,

@CacheConfig 该注解可以统一设置缓存的名称,它是类级别的注解方式。 注意:如果同时使用了 @CacheConfig 和 @Cacheable ,则将优先使用

@CacheConfig(cacheNames = "renqiang")
@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;
​
    // 优先使用此缓存名称
    @Cacheable(value = "qiang")
    @RequestMapping("list")
    public List<Student> fun(Integer id,Integer age) {
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}

@Caching 使用以上注解的时候,每一个注解都需要一个方法,如果要实现一个方法同时满足多个缓存注解的时候,就可以使用此注解。 从源码可以看出来,此注解是 @Cacheable,@CachePut,@CacheEvict 的结合体

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
​
  Cacheable[] cacheable() default {};
​
  CachePut[] put() default {};
​
  CacheEvict[] evict() default {};
​
}

例子:

@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;
​
    @Caching(
            cacheable = {@Cacheable(value = "ren")},
            put = {
                    @CachePut(value = "ren1",key = "#id"),
                    @CachePut(value = "ren2",key = "#age")
            }
    )
    @RequestMapping("list")
    public List<Student> fun(Integer id,Integer age) {
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}

三,相关配置

1,自定义 key 第一种方法,继承CachingConfigurerSupport类,重写 KeyGenerator 方法

@Configuration
public class MyConfig extends CachingConfigurerSupport {
​
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

第二种方法,实现KeyGenerator接口

@Configuration
public class MyConfig implements KeyGenerator {
​
    @Override
    public Object generate(Object target, Method method, Object... params) {
        return null;
    }
}

使用

@RestController
public class MyController {
    @Autowired
    private StudentMapper studentMapper;
​
    @Cacheable(value = "cah1",keyGenerator = "myConfig")
    @RequestMapping("fun1")
    public List fun1(){
        List<Student> list = studentMapper.selectList(null);
        return list;
    }
}

2,自定义缓存过期时间 第一种方法,继承CachingConfigurerSupport类,重写 cacheManager 方法

@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Autowired
    private RedisConnectionFactory factory;
​
    @Override
    public CacheManager cacheManager() {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheConfiguration configuration1 = configuration
                .entryTtl(Duration.ofSeconds(20)) // 设置过期事件为20秒
                .disableCachingNullValues() // 不缓存空值
//                .disableKeyPrefix() // 不使用缓存前缀
                .prefixCacheNameWith("ren"); // 自定义缓存前缀
​
        RedisCacheManager build = RedisCacheManager.builder(factory)
                .cacheDefaults(configuration1)
                .build();
        return build;
    }
}

第二种方法,配置类

@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;
​
    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheConfiguration configuration1 = configuration.entryTtl(Duration.ofSeconds(20))
                .disableCachingNullValues();
        // 设置一个初始化的缓存空间set集合
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add("cah1");
        cacheNames.add("cah2");
        // 对每个缓存空间应用不同的配置
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("cah1", configuration1); // 使用默认的缓存时间
        configMap.put("cah2", configuration.entryTtl(Duration.ofSeconds(10))); // cah2 缓存空间的缓存时间
        RedisCacheManager build = RedisCacheManager
                .builder(factory)
                .initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
                .withInitialCacheConfigurations(configMap)
                .build();
        return build;
    }
}
​

3,自定义序列化方式

@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;
​
    @Bean
    public CacheManager cacheManager() {
        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        RedisSerializationContext.SerializationPair serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer);
        // RedisCacheConfiguration默认是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value
        // 设置 key 的序列化方式为 jackson
        RedisCacheConfiguration configuration1 = RedisCacheConfiguration.defaultCacheConfig().serializeKeysWith(serializationPair);
        // 设置 value 的序列化方式为 jackson
        RedisCacheConfiguration configuration2 = configuration1.serializeValuesWith(serializationPair);
        // 设置过期时间为 20 秒
        RedisCacheConfiguration configuration3 = configuration2.entryTtl(Duration.ofSeconds(20));
​
        RedisCacheManager manager = new RedisCacheManager(redisCacheWriter, configuration3);
        return manager;
​
    }
}