Spring Cache的基本使用与分析

栏目: IT技术 · 发布时间: 4年前

内容简介:使用 Spring Cache 可以极大的简化我们对数据的缓存,并且它封装了多种缓存,本文基于 redis 来说明。1、所需依赖

概述

使用 Spring Cache 可以极大的简化我们对数据的缓存,并且它封装了多种缓存,本文基于 redis 来说明。

Spring Cache的基本使用与分析

基本使用

1、所需依赖

<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、配置文件

spring:
  # redis连接信息
  redis:
    host: 192.168.56.10
    port: 6379
  cache:
    # 指定使用的缓存类型
    type: redis
    # 过期时间
    redis:
      time-to-live: 3600000
      # 是否开启前缀,默认为true
      use-key-prefix: true
      # 键的前缀,如果不配置,默认就是缓存名cacheNames
      key-prefix: CACHE_
      # 是否缓存空置,防止缓存穿透,默认为true
      cache-null-values: true

3、Spring Cache 提供的注解如下,使用方法参见: 官方文档 ,通过这些注解,我们可以方便的操作缓存数据。

@Cacheable
@CacheEvict
@CachePut
@Caching
@CacheConfig

例如,如果需要对返回结果进行缓存,直接在方法上标注 @Cacheable 注解

@Cacheable(cacheNames = "userList") //指定缓存的名字,便于区分不同缓存
public List<User> getUserList() {
	...
}

4、redis 默认使用 jdk 序列化,需要我们配置序列化机制,自定义一个配置类,否则存入的数据显示乱码

@EnableCaching //开启缓存
@Configuration
public class MyCacheConfig {
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //指定键和值的序列化机制
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        return config;
    }
}

5、使用以上配置后,虽然乱码的问题解决了,但配置文件又不生效了,比如过期时间等,这是因为在初始化时会判断用户是否自定义了配置文件,如果自定义了,原来的就不会生效,源码如下:

private org.springframework.data.redis.cache.RedisCacheConfiguration
    determineConfiguration(ClassLoader classLoader) {
    //如果配置了,就返回自定义的配置
    if (this.redisCacheConfiguration != null) {
        return this.redisCacheConfiguration;
    }
	//没配置使用默认的配置
    Redis redisProperties = this.cacheProperties.getRedis();
    org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
        .defaultCacheConfig();
    config = config.serializeValuesWith(
        SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
    if (redisProperties.getTimeToLive() != null) {
        config = config.entryTtl(redisProperties.getTimeToLive());
    }
    if (redisProperties.getKeyPrefix() != null) {
        config = config.prefixKeysWith(redisProperties.getKeyPrefix());
    }
    if (!redisProperties.isCacheNullValues()) {
        config = config.disableCachingNullValues();
    }
    if (!redisProperties.isUseKeyPrefix()) {
        config = config.disableKeyPrefix();
    }
    return config;
}

6、所以,我们也需要手动获取 ttl、prefix 等属性,直接仿照源码就行,将配置类修改为如下:

@EnableCaching //开启缓存
@Configuration
@EnableConfigurationProperties(CacheProperties.class) //缓存的所有配置属性都在这个类里
public class MyCacheConfig {

    @Bean
    public 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.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

原理分析

在 Spring 中 CacheManager 负责创建管理 Cache,Cache 负责缓存的读写,因此使用 redis 作为缓存对应的就有 RedisCacheManager 和 RedisCache。

打开 RedisCache 源码,我们需要注意这两个方法:

1、读取数据,未加锁

@Override
protected Object lookup(Object key) {
   byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
    
   if (value == null) {
      return null;
   }
    
   return deserializeCacheValue(value);
}

2、读取数据,加锁,这是 RedisCache 中唯一一个同步方法

@Override
public synchronized <T> T get(Object key, Callable<T> valueLoader) {
   ValueWrapper result = get(key);
    
   if (result != null) {
      return (T) result.get();
   }
    
   T value = valueFromLoader(key, valueLoader);
   put(key, value);
   return value;
}

通过打断点的方式可以知道 RedisCache 默认调用的是 lookup(),因此不能应对缓存穿透,如果有相关需求,可以这样配置: @Cacheable(sync = true) ,开启同步模式,此配置只在 @Cacheable 中才有。

总结

Spring Cache 对于读模式下缓存失效的解决方案:

cache-null-values: true
@Cacheable(sync = true)
time-to-live:xxx

而对于写模式,Spring Cache 并没有相应处理,我们需要使用其它方式处理。

总的来说:

1、对于常规数据(读多写少,及时性、一致性要求不高的数据)完全可以使用 Spring Cache

2、对于特殊数据(比如要求高一致性)则需要特殊处理


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Alone Together

Alone Together

Sherry Turkle / Basic Books / 2011-1-11 / USD 28.95

Consider Facebookit’s human contact, only easier to engage with and easier to avoid. Developing technology promises closeness. Sometimes it delivers, but much of our modern life leaves us less connect......一起来看看 《Alone Together》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具