自学内容网 自学内容网

Spring 那些事【2】SpringCache 简介及应用?

一、简介

SpringCache 是Spring 提供的一整套的缓存解决方案,他不是具体的缓存实现,它只提供了一整套的接口和代码规范、配置、注解等,用于整合各种缓存方案。

Spring 从 3.1 开始定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术,并支持使用 JCache(JSR-107)注解简化我们开发。

Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合;

Cache 接口下 Spring 提供了各种 xxCache 的实现,如 RedisCache、EhCacheCache、ConsurrentMapCache 等;

  • 每次调用需要缓存功能的方法时,Spring 会检查指定参数的指定目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户,下次调用直接从缓存中获取。
  • 使用 Spring 缓存抽象时我们需要关注以下两点:
    • 1、确定方法需要被缓存以及它们的缓存策略
    • 2、从缓存中读取之前缓存存储的数据

二、使用

2.1 搭建

2.1.1 导入依赖

        <!--  引入 redis    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

2.1.2 写配置

  • 自动配置了哪些?

CacheAutoConfiguration 会导入 RedisCacheConfiguration;

自动配好了缓存管理器 RedisCacheManager

  • 配置使用 redis 作为缓存
spring.cache.type=redis

在启动类上开启注解

@EnableCaching

2.1.3 测试使用缓存

对于缓存声明,Spring的缓存抽象提供了一组Java注解:

  • @Cacheable:触发缓存填充。
  • @CacheEvict: 触发将数据从缓存删除的操作。(失效模式使用这个注解)
  • @CachePut:在不干扰方法执行的情况下更新缓存。(双写模式使用这个注解)
  • @Caching:重新组合多个缓存操作以应用于一个方法。
  • @CacheConfig:在类级别共享一些常见的缓存相关设置。
@Override
@Cacheable({"xiaolei"}) // 当前方法结果需要缓存,如果缓存有,方法不用调用。如果缓存没有,会调用方法,最后将方法的结果放入缓存
public List<CategoryEntity> getLevel1Categorys() {
    System.out.println("进入方法");
    List<CategoryEntity> list = baseMapper.selectList(new LambdaQueryWrapper<CategoryEntity>().eq(CategoryEntity::getCatLevel, 1));
    return list;
    }

2.1.4 Cacheable 细节设置

使用 Cahceable ,默认会有几个行为:

  • 1、如果缓存中有,方法不用调用
  • 2、key 默认自动生成,缓存的名字为:category::simplekey[](自动生成的 key 值)
  • 3、缓存的 value 的值,默认使用 jdk 序列化机制,将序列化后的数据存到 redis
  • 4、默认时间: -1 (不符合规范)

我们需要自定义一些需求:

1) 指定生成的缓存使用的 key : key 属性指定,接受一个 spEL.

key 的表达式可以有很多方法动态获取。例如方法名作为缓存key等(#root.method.name)

@Cacheable(value = {"xiaolei"},key = "'sysId:product:category'")

2) 指定缓存的数据的存活时间: 在配置文件中修改

spring.cache.redis.time-to-live=3600000

 

3) 将数据保存为 json 格式

  • cacheAutoConfiguration
  • RedisCacheConfiguration
@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class) // 开启属性配置的绑定功能
public class MyCacheConfig {

    @Autowired
    CacheProperties cacheProperties;

    /**
     * 配置文件中的配置没有用上.
     * 原来配置文件绑定的配置类需要让它生效.
     * @return
     */
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config=config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config=config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        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;
    }
}

原理:

通过CacheAutoConfiguration 获取到 redisCahceConfiguration,它自动初始化所有的缓存-> 每个缓存决定使用什么配置,就看配置文件中有没有,如果有,就用配置文件中的值,如果没有,就用缓存中的值。-> 想改缓存的配置,只需要给容器中放一个 RedisCacheConfiguration 即可。-> 就会想应用。

2.1.5 cacheing

默认失效模式,即删除缓存。

删除单个 key

@CacheEvict(value = "xiaolei",key = "'sysId:product:category'")

删除多个缓存

    @Caching(evict = {
            @CacheEvict(value = "xiaolei",key = "'sysId:product:category'"),
            @CacheEvict(value = "xiaolei",key = "'sysId:product:category2'")
    })

存储同一类型的数据,都可以指定同一个分区。

默认就是分区名作为缓存的名字。

2.2 原理与不足

2.2.1 读模式

  • 缓存穿透:查询一个 null 数据。解决:缓存空数据,cache-null-value = true
  • 缓存击穿:大量并发进来同时查询一个正好过期的数据。解决:加锁?默认是无加锁的。(sync =true )加锁解决
  • 缓存雪崩:大量的 key 同时过期。解决:加随机时间。(加上过期时间就行)

2.2.2 写模式(缓存与数据库一致)

  • 读写加锁
  • 引入 Cannal:感知 mysql 的更新去更新数据库
  • 都多写多:直接去数据库查询就行

原文地址:https://blog.csdn.net/mf97532/article/details/144223669

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!