性能缓慢是开发人员经常面临的一个反复出现的复杂问题,解决此类问题的最常见方法之一就是利用缓存,不过,处理缓存并不是一个容易的事情。
幸运的是,自 3.1 版本开始,Spring 引入了对 Cache 的支持。其使用方法和原理都类似于 Spring 对事务管理的支持。
接下来我们了解一下如何基于注解的方式来使用 Spring 的 Cache。
启用缓存 @EnableCaching
在使用缓存注解之前,我们必须使用 @EnableCaching
来启用缓存注解。
@EnableCaching
的使用示例如下:
@SpringBootApplication
@EnableCaching
public class UserApp {
public static void main(String[] args) {
SpringApplication.run(UserApp.class);
}
}
注解 @Cacheable
将结果存储到缓存中的方法。使用此注解后,在后续调用该方法(相同的参数)时,直接去缓存中取,无需实际执行该方法。
@Cacheable
的单个缓存使用示例如下:
@Cacheable("books")
public Book findBook(ISBN isbn) {...}
在上面的代码中,findBook
方法与名为 books
的缓存相关联。每次调用该方法时,都会检查缓存,看是否已经运行过该调用,而不必重复。虽然在大多数情况下,只有一个缓存被声明,但注解允许指定多个名字,这样就可以使用多个缓存了。使用多个缓存时,每个缓存在调用方法之前都会被检查 ---- 如果至少有一个缓存被命中,相关的值会被返回。
所有其他不包含该值的缓存也会被更新,即使该缓存方法没有被实际调用。
@Cacheable
的多个缓存使用示例如下:
// 使用多个缓存示例
@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}
序列 | 参数 | 解释 |
---|---|---|
1 | cacheNames | 指定缓存组件的名字 |
2 | key | 缓存数据时使用的key,默认使用方法参数 |
3 | keyGenerator | key 的生成器。 key 和 keyGenerator 二选一使用 |
4 | cacheManager | 可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。 |
5 | condition | 可以用来指定符合条件的情况下才缓存 |
6 | unless | 否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断 |
7 | sync | 是否使用异步模式。 |
注解 @CachePut
当需要更新缓存而不干扰方法的执行时,可以使用 @CachePut
注解。 也就是说 @CachePut
注解标注的方法总是会执行,而且会尝试将结果放入缓存(当然,是否真的会缓存还跟一些注解参数有关,比如:unless 参数)。@CachePut
更适合用于缓存的填充而不是方法流的优化。
@CachePut
拥有和@Cacheable
相同的选项。@Cacheable
的逻辑是:查找缓存 - 有就返回 -没有就执行方法体 - 将结果缓存起来;@CachePut
的逻辑是:执行方法体 - 将结果缓存起来;- 所以
@Cacheable
适用于查询数据的方法,@CachePut
适用于更新数据的方法。
@CachePut
的使用示例如下:
@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(Book book) {...}
在同一个方法上使用
@CachePut
和@Cacheable
注解通常是非常不可取的,因为它们有不同的行为。后者会使方法的调用因使用缓存而被跳过,而前者则是为了运行缓存更新而强制调用。
注解 @CacheEvict
@CacheEvict
标注在需要清除缓存的元素或类上面。当标注在一个类上面的时候,表示其中所有的方法都会触发缓存的清除操作。
@CacheEvict
具有一个额外的参数(allEntries),表示是否需要执行整个缓存的清除,而不仅仅是一个条目的清除(基于 key)。
@CacheEvict
的使用示例如下:
@CacheEvict(cacheNames="books", allEntries=true)
public void deleteBooks(long id) {...}