[英]How to load @Cache on startup in spring?
我正在使用 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;
}
我在使用@PostConstruct 时遇到了以下问题: - 即使调用了我想要缓存的方法,在从 swagger 调用它之后,它仍然没有使用缓存值。 只有在再次调用它之后。
那是因为 @PostConstruct 缓存某些东西还为时过早。 (至少我认为这是问题所在)
现在我在启动过程的后期使用它,它可以正常工作:
@Component
public class CacheInit implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
//call service method
}
}
一个选项是使用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<Book> results = dao.findAll();
results.forEach(book ->
cacheManager.getCache("books").put(book.getId(), book));
}
}
如果在启动时将 Book 的所有实例都保存在内存中是您的要求,那么您应该自己将它们存储在某个缓冲区中。 使用 findAll() 方法将它们放入缓存中意味着您必须使用 @Cacheable 注释 findAll()。 然后你必须在启动时调用 findAll() 。 但这并不意味着调用 getByIsbn(String isbn) 会访问缓存,即使在调用 findAll() 时相应的实例已经放入缓存中。 实际上不会,因为 ehcache 会将方法返回值缓存为键/值对,其中在调用方法时计算键。 因此,我不知道如何匹配 findAll() 的返回值和 getByIsbn(String) 的返回值,因为返回的类型不相同,而且键不会永远匹配所有实例。
绕过@PostConstruct
缺少参数绑定的一种方法是以下代码,其优点是一旦参数初始化就会执行:
@Bean
public Void preload(MyDAO dao) {
dao.findAll();
return null;
}
添加另一个 bean BookCacheInitialzer
在 BookCacheInitialzer 中自动装配当前 bean BookService
在 BookCacheInitialzer 伪代码的 PostConstruct 方法中
然后可以做类似的事情
class BookService {
@Cacheable("books")
public Book getByIsbn(String isbn) {
return dao.findByIsbn(isbn);
}
public List<Book> 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());
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.