spring-boot支持多種介質(zhì)來實(shí)現(xiàn)緩存功能,我們最常用的可能是redis存儲(chǔ)的方式。目前spring-boot發(fā)布了2.0.0版本較之1.5.x版本存在一些細(xì)小的差異,接下來我們比較一下兩個(gè)版本下使用方式。
一、本次案例
我們假設(shè)用戶信息(user)和產(chǎn)品信息(product)需要緩存,緩存數(shù)據(jù)都加上user和product作為key前綴,采用用戶的id和產(chǎn)品的id作為key的后綴。用戶緩存時(shí)間為30分鐘,產(chǎn)品信息緩存時(shí)間為10分鐘。
二、redis客戶端配置
無論使用spring-boot的哪個(gè)版本,我們都需要先配置redis連接,兩個(gè)版本的redis客戶端連接池使用有所不同。
| spring-boot版本 | 默認(rèn)連接池類型 |
|---|---|
| 1.5.x | jedis |
| 2.x | lettuce |
在1.5.x中,我們配置jedis連接池只需要配置 spring.redis.pool.* 開始的配置即可,如下配置
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.min-idle=0
spring.redis.pool.max-idle=8
但在2.x版本中由于引入了不同的客戶端,需要指定配置哪種連接池,如下配置
#jedis客戶端
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.max-idle=8
#lettuce客戶端
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.shutdown-timeout=100ms
除此之外還可以看到時(shí)間配置上還需要帶上對(duì)應(yīng)的單位。
三、直接配置cache參數(shù)
在1.5.x版本下提供的針對(duì)cache的配置非常少,我們能夠使用到的自動(dòng)裝配redis緩存的配置如下
#緩存的名稱集合,多個(gè)采用逗號(hào)分割
spring.cache.cache-names=
#緩存的類型,官方提供了很多,這里我們填寫redis
spring.cache.type=
在2.x版本時(shí),官方增加了更多的配置
#緩存的名稱集合,多個(gè)采用逗號(hào)分割
spring.cache.cache-names=
#緩存的類型,官方提供了很多,這里我們填寫redis
spring.cache.type=
#是否緩存null數(shù)據(jù),默認(rèn)是false
spring.cache.redis.cache-null-values=
#redis中緩存超時(shí)的時(shí)間,默認(rèn)60000ms
spring.cache.redis.time-to-live=
#緩存數(shù)據(jù)key是否使用前綴,默認(rèn)是true
spring.cache.redis.use-key-prefix=
#緩存數(shù)據(jù)key的前綴,在上面的配置為true時(shí)有效,
spring.cache.redis.key-prefix=
四、JavaConfig方式配置
通用配置方式只能滿足整個(gè)程序所有緩存都采用相同公共配置的方式,如果需要特殊處理,如我們的案列,則需要自己采用代碼的方式來配置。
采用代碼的方式,只要需要配置的是CacheMananger,采用Redis時(shí)具體實(shí)現(xiàn)我們需要使用其子類RedisCacheMananger來做配置
4.1、spring-boot 1.5.x版本
CacheManager配置
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//默認(rèn)超時(shí)時(shí)間,單位秒
redisCacheManager.setDefaultExpiration(60);
//緩存超時(shí)時(shí)間Map,key為cacheName,value為超時(shí),單位是秒
Map<String, Long> expiresMap = new HashMap<>();
//緩存用戶信息的cacheName和超時(shí)時(shí)間
expiresMap.put("user", 1800L);
//緩存產(chǎn)品信息的cacheName和超時(shí)時(shí)間
expiresMap.put("product", 600L);
redisCacheManager.setExpires(expiresMap);
return redisCacheManager;
}
cache調(diào)用代碼
@Cacheable(value = "user", key = "'user:'+#id", unless = "#result==null")
public String getUser(int id) {
//邏輯操作
}
@Cacheable(value = "product", key = "'product:'+#id", unless = "#result==null")
public String getProduct(int id) {
//邏輯操作
}
4.2、spring-boot 2.x版本
CacheManager配置
2.x版本開始,代碼方式配置變化比較大,同時(shí)增加了更多配置參數(shù)
@Bean
CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
//user信息緩存配置
RedisCacheConfiguration userCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).disableCachingNullValues().prefixKeysWith("user");
//product信息緩存配置
RedisCacheConfiguration productCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).disableCachingNullValues().prefixKeysWith("product");
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put("user", userCacheConfiguration);
redisCacheConfigurationMap.put("product", productCacheConfiguration);
//初始化一個(gè)RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
//設(shè)置CacheManager的值序列化方式為JdkSerializationRedisSerializer,但其實(shí)RedisCacheConfiguration默認(rèn)就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,所以以下注釋代碼為默認(rèn)實(shí)現(xiàn)
//ClassLoader loader = this.getClass().getClassLoader();
//JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
//RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
//RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();
//設(shè)置默認(rèn)超過期時(shí)間是30秒
defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
//初始化RedisCacheManager
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig, redisCacheConfigurationMap);
return cacheManager;
}
以上代碼中RedisCacheConfiguration類為2.x新增的配置類,增加了幾個(gè)配置項(xiàng)。這里比較奇怪的是調(diào)用它的配置方法每一次都會(huì)重新生成一個(gè)配置對(duì)象,而不是在原對(duì)象上直接修改參數(shù)值,這一點(diǎn)本人沒搞懂作者為何要選擇這種方式。
cache調(diào)用代碼
@Cacheable(value = "user", key = "#id", unless = "#result==null")
public String getUser(int id) {
//邏輯操作
}
@Cacheable(value = "product", key = "#id", unless = "#result==null")
public String getProduct(int id) {
//邏輯操作
}
這里兩點(diǎn)需要注意的地方
- 1、在Cacheable的key屬性中不在需要自己配置前綴,可以在RedisCacheConfiguration中配置。
- 2、如果Cacheable中不配置 unless = "#result==null" 屬性,而RedisCacheConfiguration中調(diào)用disableCachingNullValues()配置了不緩存null結(jié)果在出現(xiàn)null結(jié)果時(shí)會(huì)報(bào)異常,這一點(diǎn)與我一開始的理解也不太一樣,二者并非都生效的。個(gè)人理解unless配置#result==null可以決定null的值是否往cacheManager丟,而RedisCacheConfiguration的disableCachingNullValues()只在存儲(chǔ)前起到了校驗(yàn)作用,而不會(huì)因?yàn)樵O(shè)置了這個(gè)值而直接跳過null的保存。如果真是設(shè)計(jì)如此,感覺有點(diǎn)雞肋,不知道算不算一個(gè)bug。
小結(jié)
spring-boot剛剛發(fā)布了2.0.0的正式版,除了比較大的webflux之外,許多組件可能都有細(xì)節(jié)變化,在做升級(jí)的時(shí)候需要注意更改,建議先驗(yàn)證常用的功能用法后,再做升級(jí)的計(jì)劃安排。