Будет ли работать кэш в методе без прокси в Spring
Что проверяют
Понимаете ли вы, что @Cacheable работает не сам по себе, а через AOP-прокси вокруг бина. Почти всегда следом спрашивают про вызов кэшируемого метода из соседнего метода того же класса и про то, как это исправить.
Как отвечать
Нет, не будет. Аннотации кэширования обрабатывает CacheInterceptor, который Spring подключает к прокси при включённом @EnableCaching. Клиент получает из контекста не ваш объект, а прокси: он проверяет кэш, при промахе вызывает настоящий метод и сохраняет результат. Если вызов идёт мимо прокси, аннотация просто игнорируется, без ошибок и предупреждений.
Прокси бывает двух видов: JDK dynamic proxy по интерфейсу и CGLIB-подкласс. В Spring Boot по умолчанию используется CGLIB.
Случаи, когда прокси не участвует:
- самовызов:
this.findUser(id)из другого метода того же бина идёт напрямую в объект; - объект создан через
new: это не бин, контекст его не оборачивает; - нет
@EnableCaching: инфраструктура не поднята, аннотации лежат мёртвым грузом; privateиfinalметоды: CGLIB-подкласс не может их переопределить.
Как исправить самовызов:
- вынести метод в отдельный бин: самый чистый вариант;
- внедрить себя: через
@Lazyполе илиObjectProviderсвоего же типа и зватьself.findUser(id); - режим AspectJ:
@EnableCaching(mode = AdviceMode.ASPECTJ)с вплетением при компиляции или загрузке, тогда логика кэша встраивается в сам байткод; - программно:
cacheManager.getCache("users").get(id, () -> load(id)).
Где спотыкаются
Отвечают «Java не позволяет объекту вызывать свои прокси». Дело не в запрете языка: у объекта просто нет ссылки на прокси, this указывает на исходный экземпляр.
Не знают, что ошибка тихая. Приложение работает, тесты зелёные, а кэш не срабатывает ни разу, и это видно только по логам запросов к базе или по метрикам.
Сводят всё к самовызову и забывают про private методы и объекты, созданные вручную. На уточняющем вопросе «а если вызов идёт из другого бина, но метод приватный» такой ответ разваливается.