Skip to content

springboot集成springcache

Spring Boot 集成 Spring Cache 后,可以快速实现缓存读写能力。查询时优先读取缓存,新增、修改和删除操作则可以在更新数据库的同时维护缓存状态。

一、包依赖

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

二、配置文件

spring:
  cache:
    #指定缓存类型为redis
    type: redis

三、配置信息加载

需要先开启 @EnableCaching 注解。

下面示例中定义了两个 RedisCacheManager,分别用于配置不同的缓存过期时间,同时统一了序列化方式,默认使用 JSON。

@Configuration
@EnableCaching
public class RedisConfig {

    /**
     * 自定义spring cache缓存key规则
     * @return org.springframework.cache.interceptor.KeyGenerator
     * @author lcy
     * @date 2021/6/29 14:17
     **/
    @Bean
    public KeyGenerator keyGenerator(){
        return (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();
        };
    }

    /**
     * 默认spring cache配置  过期时间一个小时
     * @param connectionFactory 连接工厂
     * @return org.springframework.data.redis.cache.RedisCacheManager
     * @author lcy
     * @date 2021/6/29 14:14
     **/
    @Bean
    @Primary
    public RedisCacheManager cacheManager1Hour(RedisConnectionFactory connectionFactory){
        RedisCacheConfiguration config = instanceConfig(3600L);
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }

    /**
     * spring cache配置  过期时间一天
     * @param connectionFactory 连接工厂
     * @return org.springframework.data.redis.cache.RedisCacheManager
     * @author lcy
     * @date 2021/6/29 14:14
     **/
    @Bean
    public RedisCacheManager cacheManager1Day(RedisConnectionFactory connectionFactory){

        RedisCacheConfiguration config = instanceConfig(3600 * 24L);
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }

    /**
     * spring cache的过期时间配置
     * @param ttl 过期时间,以秒为单位
     * @return org.springframework.data.redis.cache.RedisCacheConfiguration
     * @author lcy
     * @date 2021/6/29 14:13
     **/
    private RedisCacheConfiguration instanceConfig(Long ttl){
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(serializingObjectMapper());
        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(ttl))
                .disableCachingNullValues()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));

    }

    /**
     * 获取序列化的jackson对象
     * @return com.fasterxml.jackson.databind.ObjectMapper
     * @author lcy
     * @date 2021/6/29 14:12
     **/
    public ObjectMapper serializingObjectMapper(){
        //创建jsr310包下的javaTimeModule对象,其中包含了jdk8以后的时间类型序列化和反序列化配置
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        //LocalDateTime序列化设置为yyyy-MM-dd HH:mm:ss
        LocalDateTimeSerializer localDateTimeSerializer =
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //增加序列化到配置当中
        javaTimeModule.addSerializer(LocalDateTime.class,localDateTimeSerializer);
        //LocalDateTime反序列化设置为yyyy-MM-dd HH:mm:ss
        LocalDateTimeDeserializer localDateTimeDeserializer =
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //增加反序列化到配置当中
        javaTimeModule.addDeserializer(LocalDateTime.class,localDateTimeDeserializer);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        //注册配置--jdk8类型
        objectMapper.registerModule(javaTimeModule);
        // 去掉各种@JsonSerialize注解的解析
        objectMapper.configure(MapperFeature.USE_ANNOTATIONS,false);
        // 只针对非空的值进行序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 将类型序列化到属性json字符串中
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL,JsonTypeInfo.As.PROPERTY);
        objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);

        return objectMapper;
    }
}

四、注解使用和介绍

注解支持两种使用方式:

  • 类注解:类上使用 @CacheConfig(cacheNames="xxx")。在同一个 Redis 实例中,cacheNames 建议保持明确且唯一,避免多个类共用默认值带来冲突

  • 方法注解:直接在方法上声明缓存行为,这种方式更常见,也更易于按业务粒度控制

下面是常见注解属性的说明,这些属性在多个缓存注解中都可以使用。

  • value:缓存名称,可以配置多个

  • key:缓存 Key 规则,可以使用 Spring EL 表达式;如果未设置 keyGenerator,默认使用方法参数组合生成 Key

  • condition:缓存条件,可以使用 Spring EL,返回 true 时才会进入缓存

  • keyGenerator:不配置时使用默认的 Key 生成器;如果显式指定,则填写对应 Bean 名称

  • cacheManager:不配置时使用默认的缓存管理器;如果显式指定,则填写对应 Bean 名称

例如:

//根据方法名称使用key,这里会出现不同的用户参数也会获取到相同的结果
@Cacheable(value = {"redis"}, key="#root.methodName")
//根据参数对象的id添加数据
@Cacheable(value = {"redis"}, key="#param.id")
//如果入参不是对象,根据入参的参数数组添加数据
@Cacheable(value = {"redis"}, key="#root.args[0]")
//分页获取数据
@Cacheable(value = {"product_page"},key="#root.methodName + #page+'_'+#size")

下面是常见方法注解的说明。

4.1 @Cacheable

如果缓存中已存在结果,则直接返回缓存值;如果缓存中不存在,则执行方法并将结果写入缓存。

这个注解中有一个比较常用的属性可以辅助防止缓存击穿:

  • sync:将缓存锁住,只有一个线程可以进入计算,而阻塞其它线程,直到返回结果更新到缓存当中。true为开启,默认不开启

4.2 @CachePut

在修改数据时同步更新缓存,可以使用 @CachePut。该注解会始终执行目标方法,并将执行结果写入缓存。

4.3 @CacheEvict

在删除数据时同步清理缓存,适合移除陈旧数据或不再使用的数据。

这里有一个属性需要特别注意:

  • beforeInvocation = false

    • 缓存的清除是否在方法之前执行 ,默认代表缓存清除操作是在方法执行之后执行;

    • 如果出现异常缓存就不会清除

  • beforeInvocation = true

    • 代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除

4.4 @Caching

组合注解,适合在同一个方法中同时操作多个缓存 Key 的场景。

例如:

@Caching(
            cacheable = {
                    @Cacheable(value = "redis",keyGenerator = "keyGenerator")
            },
            put = {
                    @CachePut(value = "redis",key = "#id"),
                    @CachePut(value = "redis",key = "'redis:'+#id")
            }
    )

五、使用redis session共享外部session

通过session实现登录态的控制的时候可以使用

5.1 依赖

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

5.2 配置文件

maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效。