首页 > 解决方案 > 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) 和 id 类型 (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 }
}

方法find只定义在 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 它可以工作,但这只会破坏继承。

我看到它是由于类型擦除,但我不知道如何使它正常工作?

标签: javaspringspring-cache

解决方案


Spring Cache Documentation@Cache*,接口方法上的注释不适用于基于类的代理。所以你应该添加@Cache*到每个想要缓存的类方法。

Spring 建议您仅使用 @Cache* 注释来注释具体类(和具体类的方法),而不是注释接口。您当然可以将 @Cache* 注释放在接口(或接口方法)上,但这仅在您使用基于接口的代理时才起作用。Java 注释不是从接口继承的事实意味着,如果您使用基于类的代理 (proxy-target-class="true") 或基于编织的方面 (mode="aspectj"),则缓存设置为代理和编织基础设施无法识别,并且对象不会被包装在缓存代理中,这将是非常糟糕的。


推荐阅读