需求概述
在Spring boot 中,用Redis作爲緩存,在指定方法上使用@Cacheable註解,並且在緩存時,排除特定返回值
結論
@Cacheable
中,unless
參數的作用是:函數返回值符合EL表達式條件的,不緩存。
換句話說,函數返回值符合條件的排除掉、只緩存其餘不符合條件的
高效一些,我先把結論寫在前面。感興趣的朋友可以繼續閱讀具體的論證過程。
部分代碼實現
具體方法
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
/**
* created at 2019-02-01 18:05
* @author lerry
*/
@Component
public class ForCacheService {
@Cacheable(value = "fruit", key = "#param", unless = "!#result")
public boolean isApple(String param) {
LatencyService.sleepAWhile(3);
if (param.equals("apple")) {
return true;
}
else {
return false;
}
}
}
我們定義了一個isApple
方法。想要實現的功能爲:如果入參是“apple”,則把返回結果緩存起來。如果不是,就不進行緩存。
模擬網絡延遲的代碼
import lombok.extern.slf4j.Slf4j;
/**
* created at 2019-02-01 14:52
* @author lerry
*/
@Slf4j
public class LatencyService {
/**
* 模擬延遲
* @param seconds
*/
public static void sleepAWhile(int seconds) {
try {
log.info("模擬延遲{}s開始", seconds);
Thread.sleep(seconds * 1000L);
log.info("模擬延遲{}s結束", seconds);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
測試代碼
@Autowired
private ForCacheService forCacheService;
@Test
public void testBooelanReturn() {
log.info("apple is apple ? {}", forCacheService.isApple("apple"));
log.info("apple is apple ? {}", forCacheService.isApple("apple"));
log.info("apple is apple ? {}", forCacheService.isApple("apple"));
log.info("orange is apple ? {}", forCacheService.isApple("orange"));
log.info("orange is apple ? {}", forCacheService.isApple("orange"));
log.info("orange is apple ? {}", forCacheService.isApple("orange"));
}
測試返回結果
- 模擬延遲3s開始
- 模擬延遲3s結束
- apple is apple ? true
- apple is apple ? true
- apple is apple ? true
- 模擬延遲3s開始
- 模擬延遲3s結束
- orange is apple ? false
- 模擬延遲3s開始
- 模擬延遲3s結束
- orange is apple ? false
- 模擬延遲3s開始
- 模擬延遲3s結束
- orange is apple ? false
可以看到,當參數是apple
時,第一次訪問,程序執行了3秒延遲的代碼。但是第二次、第三次執行時,直接從緩存獲取了結果。 當參數是orange
時,沒有進行緩存。第二次、第三次訪問,都進行了3秒延遲。
unless的詳細解釋
unless = "!#result"
其中#result
,代指函數的返回值。非(!)(返回值),都進行緩存。 就是說,當返回值不是false時,才進行緩存。
這樣解釋,可能比較繞。接下來,我換個寫法,再解釋一遍。
我們實現一個String類型返回值的函數。這個函數很簡單,返回值就是入參:
@Cacheable(value = "str", key = "#param", unless = "#result eq 'abc'")
public String stringDemo(String param) {
LatencyService.sleepAWhile(3);
return param;
}
測試代碼如下:
@Test
public void testStrReturn() {
log.info("123:{}", forCacheService.stringDemo("123"));
log.info("123:{}", forCacheService.stringDemo("123"));
log.info("123:{}", forCacheService.stringDemo("123"));
log.info("456:{}", forCacheService.stringDemo("456"));
log.info("456:{}", forCacheService.stringDemo("456"));
log.info("456:{}", forCacheService.stringDemo("456"));
log.info("abc:{}", forCacheService.stringDemo("abc"));
log.info("abc:{}", forCacheService.stringDemo("abc"));
log.info("abc:{}", forCacheService.stringDemo("abc"));
}
測試結果如下:
- 模擬延時3s開始
- 模擬延時3s結束
- 123:123
- 123:123
- 123:123
- 模擬延時3s開始
- 模擬延時3s結束
- 456:456
- 456:456
- 456:456
- 模擬延時3s開始
- 模擬延時3s結束
- abc:abc
- 模擬延時3s開始
- 模擬延時3s結束
- abc:abc
- 模擬延時3s開始
- 模擬延時3s結束
- abc:abc
可以很明顯的看到,除了abc
沒有被緩存,其餘不等於abc
的字符串,都被緩存了。
unless = "#result eq 'abc'
的含義爲:
函數返回值符合條件的,不緩存。
或者說:
除了符合el表達式條件的,其餘的都進行緩存。