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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27940704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 05:20:41  来源:igfitidea点击:

How to load @Cache on startup in spring?

javaspringspring-cache

提问by membersound

I'm using spring-cache to improve database queries, which works fine as follows:

我正在使用 spring-cache 来改进数据库查询,它的工作原理如下:

@Bean
public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("books");
}

@Cacheable("books")
public Book getByIsbn(String isbn) {
    return dao.findByIsbn(isbn);
}

But now I want to prepopulate the full book-cache on startup. Which means I want to call dao.findAll()and put all values into the cache. This routine shall than only be scheduled periodically.

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

But how can I explicit populate a cache when using @Cacheable?

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

采纳答案by Loki

Just use the cache as before, add a scheduler to update cache, code snippet is below.

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

@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);
        }
    }
}

Make sure your KeyGeneratorwill return the object for one parameter (as default). Or else, expose the putToCachemethod in BookServiceto avoid using cacheManager directly.

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

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

回答by Olivier Meurice

If having all instances of Book in memory at startup is your requirement than you should store them in some buffer yourself. Putting them in the cache with the findAll() method means that you must annotate findAll() with @Cacheable. Then you would have to call findAll() at startup. But that does not mean that calling getByIsbn(String isbn) will access the cache even if the corresponding instance has been put in the cache when calling findAll(). Actually it won't because ehcache will cache method return value as a key/value pair where key is computed when method is called. Therefore I don't see how you could match the return value of findAll() and return value of getByIsbn(String) because returned types are not the same and moreover key won't never match for all your instances.

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

回答by Dheerendra Kulkarni

Add another bean BookCacheInitialzer

添加另一个 bean BookCacheInitialzer

Autowire the current bean BookService in BookCacheInitialzer

在 BookCacheInitialzer 中自动装配当前 bean BookService

in PostConstruct method of BookCacheInitialzer pseudo code

在 BookCacheInitialzer 伪代码的 PostConstruct 方法中

Then can do something like

然后可以做类似的事情

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());
    }

}   

}

}

回答by Sameer Shah

As Olivier has specified, since spring caches output of function as a single object, using @cacheable notation with findAll will not allow you to load all objects in cache such that they can later be accessed individually.

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

One possible way you can load all objects in cache is if caching solution being used provides you a way to load all objects at startup. E.g solutions like NCache/ TayzGridprovides Cache startup loader feature, that allows you to load cache at startup with objects using a configurable cache startup loader.

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

回答by mladzo

An option would be to use the CommandLineRunnerfor populating the cache on startup.

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

From official CommandLineRunner documentation, it is an:

从官方 CommandLineRunner 文档中,它是一个:

Interface used to indicate that a bean should runwhen it is contained within a SpringApplication.

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

Hence, we just need to retrieve the list of all available books and then, using CacheManager, we populate the book cache.

因此,我们只需要检索所有可用书籍的列表,然后使用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));
    }
}

回答by Andrea Calin

I have encountered the following problem when using @PostConstruct: - even though the method I wanted to be cached was called, after calling it from swagger, it still didn't use the cached value. Only after called it once more.

我在使用@PostConstruct 时遇到了以下问题: - 即使调用了我想要缓存的方法,在从 swagger 调用它之后,它仍然没有使用缓存值。只有在再次调用它之后。

That was because @PostConstruct is too early for caching something. (At least I think that was the issue)

那是因为 @PostConstruct 缓存某些东西还为时过早。(至少我认为这是问题所在)

Now I'm using it more late in the startup process and it works without problems:

现在我在启动过程的后期使用它,它可以正常工作:

@Component
public class CacheInit implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
       //call service method
    }

}