Spring Cache: проблема вызова @Cacheable метода внутри класса
Быстрый ответ
@Cacheable
не работает при внутренних вызовах методов в рамках одного и того же бина. Кэширование активируется только при вызове методов через прокси бина. Для того чтобы использовать кэширование в рамках одного класса, используйте внедрение ссылки на сам бин или распределите функционал между разными бинами.
Вот пример с внедрением ссылки на сам бин и внедрением поля:
@Service
public class MyService {
// Ссылка на сам объект
@Autowired
private MyService proxy;
public void callCachedMethod() {
proxy.cachedOperation(); // Вызывается метод с кэшированием
}
@Cacheable(cacheNames = "myCache")
public String cachedOperation() {
return "пример кэширования";
}
}
Внедрение через конструктор может привести к проблемам, таким как циклической зависимости и самоссылкам. Вместо этого лучше использовать внедрение через поля или сеттеры.
Разбор работы Spring Cache при внутренних вызовах
Spring использует динамические прокси для перехвата вызовов методов с аннотацией @Cacheable
. Главное: прокси функционируют только при вызовах снаружи бина, а не внутри, аналогично вызову приватного метода из других классов без использования прокси.
Прокси и их применение
В Spring AOP выбор между использованием динамических прокси JDK и CGLIB зависит от конфигурации. JDK прокси кешируют только вызовы интерфейсных методов, CGLIB прокси — и методы классов. Однако оба варианта не кешируют внутренние вызовы в бине, так как они не взаимодействуют с прокси, и @Cacheable
не срабатывает.
Как обеспечить кэширование при внутренних вызовах
Хорошей практикой для решения этой проблемы будет установка proxyTargetClass
в true
в настройках кэша или использования внедрённого бина. Будьте внимательны к циклическим зависимостям при автовнедрении ApplicationContext
. Если эти методы не подходят, можно разнести методы по разным бинам.
Дополнительные инструменты: AspectJ и отладка кэша
Для сложных ситуаций с вызовами внутри класса вы можете использовать AspectJ, который встраивается на этапе компиляции и обходит необходимость использования прокси.
Для понимания, как работает ваш кэш, полезна отладка. Старайтесь логировать попадания и промахи кэша для анализа его эффективности.
Визуализация
Представьте себе, что внутренние вызовы обходят прокси, как если бы вы пытались связаться с другом, стоящим рядом, через посредство почты. Если передать ему сообщение напрямую, оно не "закэшируется" в системе доставки, как и вызов метода без прокси.
Обычный вызов без кэширования:
🚶 -> 📝 -> 🚪 (заходит и выходит из того же 🏠)
Ожидаемое Spring'ом действие:
🚶 -> 📝 -> 📮 -> 🚶 (сообщение доставляется из другого 🏠)
Система кэширования активируется только при использовании "почтовой системы".
Решение:
🚶 -> 📝 -> 🚪 -> 📮 -> ✅ -> 🚶 (сообщение доставлено в том же 🏠, но через почту)
Для активации кэширования нужно следовать установленным правилам передачи сообщений, даже если это происходит в рамках одного класса.
Оптимизация Spring Cache: возможности и ограничения
Кэширование – это эффективный инструмент для улучшения производительности приложений, но только при надлежащем и стратегическом использовании. Вот несколько методик для оптимальной конфигурации кэша.
Подходы к настройке кэша
Применяйте @CacheConfig
на уровне класса, чтобы создать чёткий шаблон кэширования, что упрощает чтение и управление настройками.
Продвинутые методы кэширования: самоссылка, планирование мощности и стратегии очистки
Политики очистки кэша реализуются с помощью @CacheEvict
, что позволяет удалять устаревшие данные, освобождая место в памяти. Также можно настроить планирование мощности кэширования.
В крайних ситуациях можно использовать ApplicationContext
, как показано ниже:
@Autowired
private ApplicationContext applicationContext;
public String invokeCacheable() {
MyService beanProxy = applicationContext.getBean(MyService.class);
return beanProxy.invokeCacheable();
}
Но следует избегать частого использования ApplicationContext, так как это может создавать тесные и сложноуправляемые связи в приложении.
Полезные материалы
- Официальная документация Spring по кэшированию — подробное руководство по кэшированию в Spring.
- Подробное руководство по кэшированию данных в Spring — вводное руководство по использованию Spring Cache.
- Реализация кэша в Spring Boot — пошаговое руководство по интеграции кэширования в Spring Boot.
- Учебник по работе с кэшированием в Spring на DigitalOcean — всё о
@Cacheable
и других аннотациях кэширования. - Принципы работы кэширования в Spring на DZone — детальный обзор механизма кэширования в Spring.