在Spring boot 中,用Redis作为缓存,在指定方法上使用@Cacheable注解,并且在缓存时,排除特定返回值
@Cacheable
中,unless
参数的作用是:函数返回值符合表达式条件的,veto(否决)、不缓存
换句话说,函数返回值符合条件的排除掉、只缓存其余不符合条件的
高效一些,我先把结论写在前面。感兴趣的朋友可以继续阅读具体的论证过程。
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 = "!#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表达式条件的,其余的都进行缓存。
Spring Expression Language (SpEL) expression used to veto method caching.
Unlike condition, this expression is evaluated after the method has been called and can therefore refer to the result.
Default is “”, meaning that caching is never vetoed.
翻译:
用于否决方法缓存的 Spring表达式语言(SpEL) 表达式。
与condition不同,这个表达式是在调用方法之后计算的,因此可以引用结果(方法返回结果)。
默认值是“”,这意味着缓存从未被否决。
所以,可以这样理解:
函数返回值符合表达式条件的,veto(否决)、不缓存