Spring Cache работает с вложенным методом
У меня есть один метод для вызова другого метода @Cacheable, как это:
public ItemDO findMethod2(long itemId) {
this.findMethod1(itemId);
...
}
@Cacheable(value = "Item", key="#itemId", unless="#result == null")
public ItemDO findMethod1(long itemId) {
...
}
Кэш работает хорошо, если я вызываю findMethod1() напрямую. Однако, когда я вызываю findMethod2 (), кэш в findMethod1() полностью игнорируется.
Может ли это быть уловка, созданная JVM, которая встроила findMethod1() в findMethod2()?
Кто-нибудь сталкивался с подобной проблемой?
Спасибо!
1 ответ
Это не трюк JVM, т.е. findMethod1()
не вводится внутрь findMethod2()
или что-нибудь в этом роде.
Проблема в том, что ваш код обходит "Прокси", который Spring создает вокруг вашего класса приложений (содержащий findMethod1()
) для @Cacheable
аннотаций.
Подобно аннотациям Transactional и базовой инфраструктуре Spring, при наличии интерфейса Spring по умолчанию создает динамический прокси-сервер JDK (стиль AOP) для "перехвата" вызова метода и применения "совета" (в данном случае определяемого типом аннотации). Кеширование). Однако, как только целевой объект вызывается из перехватчика (Proxy), действующего от имени целевого объекта, чтобы применить рекомендацию, поток теперь выполняется в контексте целевого объекта, поэтому происходят любые последующие вызовы метода из целевого объекта. непосредственно на сам целевой объект.
Это выглядит примерно так...
caller -> Proxy -> findMethod2() -> findMethod1()
В идеале, что вы хотите, это...
caller -> Proxy -> findMethod2() -> Proxy -> findMethod1()
Тем не менее, поток уже выполняется в контексте "целевого" объекта один раз внутри findMethod2()
, так что вы получите первый стек вызовов.
Весенний док объясняет это лучше здесь.
Далее в документе указываются пути решения этой проблемы, наиболее благоприятным является рефакторинг вашего кода, чтобы гарантировать, что вызывающая сторона проходит через прокси-перехватчик для вызова 2-го метода (т.е. findMethod1()
).
Я также собираюсь другое решение этой проблемы было бы использовать полномасштабный AspectJ
использование компилятора и ткача байт-кода в процессе сборки приложения для изменения фактического целевого объекта, чтобы последующие вызовы из целевого объекта перехватывали и применяли рекомендации соответствующим образом.
См. Весенние документы о компромиссах между Spring AOP
и полный AspectJ
, а также как использовать полноценный AspectJ в ваших приложениях Spring.
Надеюсь это поможет.
Ура!
Другое решение, которое я считаю удобным, - это использование @Resource, а затем вызов цели (method1 в вашем случае) с использованием этой ссылки на ресурс по /questions/23894779/spring-cache-cacheable-ne-rabotaet-pri-vyizove-iz-drugogo-metoda-togo-zhe-komponenta/23894815#23894815