【问题标题】:Spring cache not working for overriden methods in a subclassSpring缓存不适用于子类中的覆盖方法
【发布时间】:2019-01-25 19:56:53
【问题描述】:

我无法让 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 添加一个特定调用,它可以工作,但这只会破坏继承。

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

【问题讨论】:

  • LocationManager.create的代码在哪里?
  • 为了简化,我没有包含它。抱歉,我刚刚更改了示例方法并完成了一些缺少的参数。
  • 分享到 github repo 的链接比在文本中解析代码要方便得多。这是什么版本的 Spring Framework?
  • Spring 4.3.14 和 4.1.9 也是。嗯,这个例子很简单。

标签: java spring spring-cache


【解决方案1】:

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

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

【讨论】:

  • 我没有说任何时候我都在使用基于类的代理。我确实没有使用它们,这是默认配置,因此注释接口是正确的。无论如何,我已经尝试将注释放在类中并且没有任何改变。我检查了该对象是否用代理包装。但问题在于检测方法是否具有缓存注释的 Spring 代码,由于类型擦除,该方法被遗漏。
猜你喜欢
  • 2018-05-27
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 2015-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
相关资源
最近更新 更多