Кэш Spring не работает для переопределенных методов в подклассе

Я не могу заставить кеш Spring работать правильно с методами, переопределенными в подклассе, также реализованном в суперклассе. Например, у меня есть этот абстрактный сервис:

public interface CrudService<E, I> {
  void deleteById(I id);
  E create(E item);
}
public abstract class CrudServiceImpl<E, I> {
  void deleteById(I id) { // do things }
  ...
}

У меня есть несколько сервисов, расширяющих этот абстрактный класс для разных сущностей (E) и типов идентификаторов (I). Я хочу кэшировать только один из них:

public interface LocationService extends CrudService<Location, String> {
   @CacheEvict("location")
   @Override
   void deleteById(String id);

   @Cacheable("location")
   List<Location> find();
}

@Service
public class LocationServiceImpl extends CrudServiceImpl<Location, String> implements LocationService {
   public List<Location> find() { // do things }
}

Метод найти определен только в LocationService, а не в абстрактном классе. Когда я вызываю эти методы из компонента, который также имеет абстрактный класс:

public abstract class CrudManager<E, I> {
    @Autowired
    private CrudService<E, I> crudService; 

   public void doDelete(I id) {
      crudService.deleteById(id);
   }
}

@Component
public class LocationManager extends CrudManager<Location, String> {
   @Autowired
   private LocationService locationService;

   public List<Location> doFind() {
      return locationService.find();
   }
}

Я подтвердил, что когда вызывается LocationManager.doFind, он запускает операции кэширования, определенные в LocationService, а LocationManager.doDelete — нет.

Я отлаживал до тех пор, пока AbstractFallbackCacheOperationSource.getCacheOperations не понял, что метод поиска операций:

public default void com.ontech.plantcore.service.LocationService.deleteById(java.lang.Object)

с targetClass = LocationServiceImpl.class вместо моего аннотированного метода LocationService.deleteById(java.lang.String). Таким образом, ClassUtils.getMostSpecificMethod не может найти аннотированный метод, и никакие операции не возвращаются. Это происходит с Spring 4.3.14 и 4.1.9.

Если я добавлю конкретный вызов в LocationManager к locationService.deleteById, он сработает, но это просто разрушит наследование.

Я вижу, что это связано со стиранием текста, но я не знаю, как заставить его работать правильно?

Где код про LocationManager.create?

Manuel Jordan 25.01.2019 21:05

Я не включил его для упрощения. Извините, я только что изменил пример метода и дополнил некоторые недостающие параметры.

Aníbal 25.01.2019 21:18

Поделиться ссылкой на репозиторий github было бы гораздо удобнее, чем анализировать код в тексте. Какая это версия Spring Framework?

Stephane Nicoll 29.01.2019 22:34

Spring 4.3.14 и 4.1.9 тоже. Ну, пример довольно простой.

Aníbal 30.01.2019 10:27
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
4
999
1

Ответы 1

В Документация по кэшу Spring говорится, что аннотация @Cache* к методу интерфейса не работает с прокси-серверами на основе классов. Таким образом, вы должны добавить @Cache* к каждому методу класса, который хотите кэшировать.

Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Cache* annotation, as opposed to annotating interfaces. You certainly can place the @Cache* annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies. The fact that Java annotations are not inherited from interfaces means that if you are using class-based proxies (proxy-target-class = "true") or the weaving-based aspect (mode = "aspectj"), then the caching settings are not recognized by the proxying and weaving infrastructure, and the object will not be wrapped in a caching proxy, which would be decidedly bad.

Я никогда не говорил, что использую прокси на основе классов. Я действительно не использую их, что является конфигурацией по умолчанию, поэтому правильно аннотировать интерфейсы. Во всяком случае, я уже пробовал размещать аннотации в классах и ничего не меняет. Я проверил, что объект обернут прокси. Но проблема в коде Spring, который определяет, есть ли у метода аннотации кеша, что из-за стирания типа метод пропускается.

Aníbal 15.02.2019 17:18

Другие вопросы по теме