Reputation: 925
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
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