内容简介:使用 Spring Cache 可以极大的简化我们对数据的缓存,并且它封装了多种缓存,本文基于 redis 来说明。1、所需依赖
概述
使用 Spring Cache 可以极大的简化我们对数据的缓存,并且它封装了多种缓存,本文基于 redis 来说明。
基本使用
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、对于特殊数据(比如要求高一致性)则需要特殊处理
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用动态分析技术分析 Java
- 使用动态分析技术分析 Java
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
- 使用Miasm分析Shellcode
- fishhook使用场景&源码分析
- WMRouter使用和源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!