如何在春季启动时加载@Cache?

我正在使用spring-cache来改进数据库查询,其工作正常如下:

@Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("books"); } @Cacheable("books") public Book getByIsbn(String isbn) { return dao.findByIsbn(isbn); } 

但现在我想在启动时预先填充完整的书籍缓存。 这意味着我想调用dao.findAll()并将所有值放入缓存中。 此例程不应仅定期安排。

但是,在使用@Cacheable时,如何显式填充缓存?

只需像以前一样使用缓存,添加一个调度程序来更新缓存,下面是代码片段。

 @Service public class CacheScheduler { @Autowired BookDao bookDao; @Autowired CacheManager cacheManager; @PostConstruct public void init() { update(); scheduleUpdateAsync(); } public void update() { for (Book book : bookDao.findAll()) { cacheManager.getCache("books").put(book.getIsbn(), book); } } } 

确保您的KeyGenerator将返回一个参数的对象(默认情况下)。 或者,在BookService公开putToCache方法以避免直接使用cacheManager。

 @CachePut(value = "books", key = "#book.isbn") public Book putToCache(Book book) { return book; } 

如果启动时在内存中拥有Book的所有实例都是您的要求,那么您应该自己将它们存储在某个缓冲区中。 使用findAll()方法将它们放入缓存意味着您必须使用@Cacheable注释findAll()。 然后你必须在启动时调用findAll()。 但这并不意味着调用getByIsbn(String isbn)将访问缓存,即使在调用findAll()时相应的实例已放入缓存中。 实际上它不会因为ehcache将方法返回值缓存为键/值对,其中在调用方法时计算键。 因此,我看不出你如何匹配findAll()的返回值和getByIsbn(String)的返回值,因为返回的类型不一样,而且key对于你的所有实例都不会永远不匹配。

一个选项是使用CommandLineRunner在启动时填充缓存。

从官方CommandLineRunner文档,它是:

用于指示bean在SpringApplication中包含时应运行接口

因此,我们只需要检索所有可用书籍的列表,然后使用CacheManager填充书籍缓存。

 @Component public class ApplicationRunner implements CommandLineRunner { @Autowired private BookDao dao; @Autowired private CacheManager cacheManager; @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("books"); } @Override public void run(String... args) throws Exception { List results = dao.findAll(); results.forEach(book -> cacheManager.getCache("books").put(book.getId(), book)); } } 

正如Olivier所指定的那样,由于spring将函数输出缓存为单个对象,因此使用带有findAll的@cacheable表示法将不允许您加载缓存中的所有对象,以便以后可以单独访问它们。

在缓存中加载所有对象的一种可能方法是,如果使用缓存解决方案为您提供了一种在启动时加载所有对象的方法。 例如,像NCache / TayzGrid这样的解决方案提供了缓存启动加载器function,允许您使用可配置缓存启动加载器在启动时使用对象加载缓存。

添加另一个bean BookCacheInitialzer

在BookCacheInitialzer中自动assembly当前的Bean BookService

在BookConstruct方法中的BookCacheInitialzer伪代码

然后可以做类似的事情

 class BookService { @Cacheable("books") public Book getByIsbn(String isbn) { return dao.findByIsbn(isbn); } public List books; @Cacheable("books") public Book getByIsbnFromExistngBooks(String isbn) { return searchBook(isbn, books); } 

}

  class BookCacheInitialzer { @Autowired BookService service @PostConstruct public void initialize() { books = dao.findAll(); service.books = books; for(Book book:books) { service.getByIsbnFromExistngBooks(book.getIsbn()); } } 

}