Я использую Spring Boot 3.x, и аннотация @Cacheable
работает с методами, которые не являются общедоступными. Это странно, поскольку в документации четко указано:
Видимость метода и аннотации кеширования
При использовании прокси следует применять аннотации кеширования только к общедоступным методам. Если вы снабжаете этими аннотациями защищенные, частные или видимые для пакета методы, ошибка не возникает, но аннотированный метод не отображает настроенные параметры кэширования. Рассмотрите возможность использования AspectJ (см. остальную часть этого раздела), если вам нужно аннотировать закрытые методы, поскольку он меняет сам байт-код.
Вот мой пример услуги:
@Service
public class CacheService {
@Cacheable("cache1")
String foo() {
System.out.println("string");
return "string";
}
}
И очень простой тест:
@SpringBootTest
class ApplicationTests {
@Autowired
private CacheService cacheService;
@Test
void test2() {
cacheService.foo();
cacheService.foo();
cacheService.foo();
cacheService.foo();
cacheService.foo();
cacheService.foo();
}
}
В Spring Boot 2.7 вывод консоли показывает 6 «строковых» сообщений, но в версии 3.0.0 (и выше) отображается только 1 «строка».
Есть ли у вас идеи, что изменилось и где в документации описано такое поведение?
Это всплыло во время презентации компании, и я был уверен, что понял поведение этой аннотации, поэтому ищу аргументы, подтверждающие это :)
РЕДАКТИРОВАТЬ
Вот пример с ветвью spring-2.7
и spring-3.0
.
Вы не используете прокси-серверы интерфейса JDK Spring по умолчанию, а напрямую проксируете класс, т. е. результатом будет прокси-сервер CGLIB, который по сути является подклассом исходного класса. То есть непубличные методы могут и будут проксироваться и делегироваться исходным целевым методам, если они не являются частными. Это ожидаемое поведение прокси Spring. Для справки:
ПРИМЕЧАНИЕ
Из-за того, что среда АОП Spring основана на прокси-сервере, вызовы внутри целевого объекта по определению не перехватываются. Для прокси-серверов JDK можно перехватывать только вызовы методов открытого интерфейса на прокси-сервере. С помощью CGLIB перехватываются вызовы общедоступных и защищенных методов на прокси-сервере (и даже методов, видимых пакетом, если необходимо). Однако обычное взаимодействие через прокси всегда должно осуществляться с помощью публичных подписей.
Учитывая следующую модификацию вашего сервиса и теста:
@Service
public class CacheService {
@Cacheable("cache1")
public String publicMethod() {
System.out.println("public");
return "public";
}
@Cacheable("cache1")
String nonPublicMethod() {
System.out.println("non-public");
return "non-public";
}
}
@SpringBootTest
class ApplicationTests {
@Autowired
private CacheService cacheService;
@Test
void test2() {
cacheService.publicMethod();
cacheService.publicMethod();
cacheService.publicMethod();
cacheService.nonPublicMethod();
cacheService.nonPublicMethod();
cacheService.nonPublicMethod();
}
}
Я вижу следующее поведение, в зависимости от версии Spring Boot и от того, закомментирую ли я аннотацию @Cacheable
одним из двух методов:
@Cacheable
публичный
Имеет @Cacheable
непубличный доступ
Поведение
2,7
да
нет
Кэшируется только публичный метод
2,7
нет
да
Ни один метод не кэшируется
2,7
да
да
Кэшируется только публичный метод
3.0
да
нет
Кэшируется только публичный метод
3.0
нет
да
Кэшируется только закрытый метод.
3.0
да
да
Кэшируются как публичные, так и закрытые методы.
Вкратце: в Spring 2.7 кэширование применяется только к общедоступным методам. В Spring 3.0 кеширование работает и для непубличных (но нечастных) методов, несмотря на рекомендацию — на самом деле это не что иное, — использовать аннотацию только для публичных API.
Некоторые исследования в репозитории Spring Core выявили проблему № 25582: «@Transactional не работает с защищенными пакетами методами прокси CGLib» . В Spring 6.0 он был закрыт коммитом 37bebeaaaf с комментарием: «Принимать защищенные @Transactional/Cacheable методы на прокси-серверах CGLIB». То есть, он был изменен намеренно, что, по моему мнению, имеет смысл, поскольку делает Spring более универсальным в особых ситуациях и работает аналогичным образом в других контекстах прокси Spring, например. для Spring AOP.
Нет, поведение Spring AOP на основе прокси было таким всегда. Что касается настроек Spring Boot по умолчанию в целом или @Cacheable
в частности, я не знаю, так как не являюсь активным пользователем Spring. Возможно, кто-то другой сможет просветить вас больше. Если вы сможете опубликовать минимальный воспроизводитель (проект Maven) на GitHub с двумя ветками для Boot 2.7 и 3.0, я смогу рассмотреть его поближе.
вот пример: github.com/rechandler12/spring-cachable-example
Спасибо. Я поэкспериментировал и немного исследовал ваш MCVE, а затем значительно обновил свой ответ.
Это поведение изменилось в Spring Framework 6.0/Spring Boot 3.0?