Spring-кэшируемые аннотации не работают до завершения развертывания

У меня есть следующие классы и интерфейсы (упрощены, чтобы прояснить ситуацию).

@Serivce
class Service1 {

    @Cacheable("cacheName")
    public Metadata preLoadMetadata() {
        // load data from database
    }
}

@Service
class Service2 implements Loader {

    @Autowired @Lazy
    private Service1 service1;

    @Overrride
    public CacheWrapper getCacheWrapper() {
        return new CacheWrapper(() -> service1.preLoadMetadata());
    }
}

interface Loader {
    CacheWrapper getCacheWrapper();
}

class CacheWrapper {

    private Callable callable;

    public CacheWrapper(Callable callable) {
         this.callable = callable;
    }

    public getCallable() {
         return callable;
    }
}

Компонент Spring, отвечающий за загрузку кэша во время развертывания.

@Component
class LoadCache {

    @Autowired
    private List<Loader> allLoaders;

    @PostConstruct
    public void load() {
        allLoaders.getCacheWrapper().getCallable().call();
    }
}

preLoadMetadata() не сохраняет данные в кеше, но выполняет. После завершения развертывания, и я вызываю тот же метод preLoadMetadata() снова, затем он сохраняет данные в кеш.

Почему @Cacheable не работает во время развертывания?

Если я вручную использую put метод Cache заполнить кеш внутри метода с пометкой @PostConstructЯ могу сделать это успешно во время развертывания.

Я использую Tomcat в качестве сервера.
Я использую кеш Couchbase за абстракцией Spring кеша.

2 ответа

Решение

Основная причина не работает Cacheable аннотация:afterSingletonsInstantiated() метод устанавливает initialized признак истины только когда BeanFactory создал все бобы. Если initialized флаг равен false, операции кэширования не выполняются. Здесь, внутри пост-конструкции, когда мы начинаем предварительную загрузку кэшей, очень вероятно, что LoadCache не последний компонент, который будет создан. Следовательно, операции кэширования не выполняются (при использовании аннотаций кэширования).

Следовательно, EventListener аннотация в ответ на ContextRefreshEvent Лучше всего использовать в этом случае, который гарантирует, что контекст запущен, а затем происходит только загрузка кэшей.

Если вы хотите предварительно загрузить кэш, я предлагаю вам использовать ApplicationListener который будет выполняться после запуска вашего приложения:

@Component
public class CacheInitializer implements ApplicationListener<ContextRefreshedEvent>{

    @Autowired
    private List<Loader> allLoaders;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        allLoaders.getCacheWrapper().getCallable().call();
    }
}
Другие вопросы по тегам