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、对于特殊数据(比如要求高一致性)则需要特殊处理


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

查看所有标签

猜你喜欢:

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

Intel汇编语言程序设计

Intel汇编语言程序设计

Kip Irvine / 电子工业出版社 / 2007-9-1 / 61.00元

《国外计算机科学教材系列•Intel汇编语言程序设计(第5版)》全面细致地讲述了汇编语言程序设计的各个方面。从微处理器体系结构、工作机制到指令集;从最基本的编译器链器的使用到高级过程、结构和宏的使用;从用纯汇编编写程序到用C/C++等最新编译器与汇编的混合接口编程;从16位实模式下BIOS、DOS实模式文本及图形程序设计到32位保护模式的Windows程序设计;从磁盘基础知识到Intel指令编码、......一起来看看 《Intel汇编语言程序设计》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

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

HSV CMYK互换工具