Aníbal
Aníbal

Reputation: 925

Spring cache not working for overriden methods in a subclass

I can't get Spring cache to work correctly with methods overriden in a subclass also implemented in the superclass. For example, I have this abstract service:

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 }
  ...
}

I have several services extending this abstract class for different entities (E) and id types (I). I only want to cache one of them:

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 }
}

Method find is only defined in LocationService, not in the abstract class. When I call those methods from a component that also has an abstract class:

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();
   }
}

I have confirmed that when LocationManager.doFind is called, it triggers cache operations defined in LocationService, but LocationManager.doDelete don't.

I have debugged until AbstractFallbackCacheOperationSource.getCacheOperations to realize that the method it's searching operations for is:

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

with targetClass = LocationServiceImpl.class, instead of my annotated method that is LocationService.deleteById(java.lang.String). So ClassUtils.getMostSpecificMethod fails to find the annotated method and no operations are returned. It happens with Spring 4.3.14 and 4.1.9.

If I add an specific call in LocationManager to locationService.deleteById it works, but that's just ruining the inheritance.

I see it's dued to type erasure, but I don't know how to make it works correctly?

Upvotes: 3

Views: 2499

Answers (1)

aburakc
aburakc

Reputation: 109

In Spring Cache Documentation says @Cache* annotation on interface method does not work with class-based proxies. So you should add @Cache* to each class method that want to 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.

Upvotes: 1

Related Questions