【问题标题】:Spring Cache not working for abstract classesSpring Cache 不适用于抽象类
【发布时间】:2016-08-26 22:26:27
【问题描述】:

我正在尝试在抽象类中使用 Spring Cache,但它不起作用,因为据我所知,Spring 正在抽象类上搜索 CacheNames。我有一个使用服务层和 dao 层的 REST API。这个想法是为每个子类设置不同的缓存名称。

我的抽象服务类如下所示:

    @Service
    @Transactional
    public abstract class AbstractService<E> {

...

    @Cacheable
    public List<E> findAll() {
        return getDao().findAll();
    }
}

抽象类的扩展如下所示:

@Service
@CacheConfig(cacheNames = "textdocuments")
public class TextdocumentsService extends AbstractService<Textdocuments> {
...
}

所以当我使用这段代码启动应用程序时,Spring 给了我以下异常:

Caused by: java.lang.IllegalStateException: No cache names could be detected on 'public java.util.List foo.bar.AbstractService.findAll()'. Make sure to set the value parameter on the annotation or declare a @CacheConfig at the class-level with the default cache name(s) to use.
    at org.springframework.cache.annotation.SpringCacheAnnotationParser.validateCacheOperation(SpringCacheAnnotationParser.java:240) ~[spring-context-4.1.6.RELEASE.jar:?]

我认为这是因为 Spring 在抽象类上搜索 CacheName,尽管它是在子类上声明的。

尝试使用

 @Service
 @Transactional
 @CacheConfig
        public abstract class AbstractService<E> {
    }

导致同样的异常;使用

 @Service
 @Transactional
 @CacheConfig(cacheNames = "abstractservice")
        public abstract class AbstractService<E> {
    }

也不例外,但是 Spring Cache 为每个子类使用相同的缓存名称,并忽略子类上定义的缓存名称。有什么想法可以解决这个问题吗?

【问题讨论】:

  • 您找到解决问题的方法了吗?
  • 很遗憾没有。
  • 那太糟糕了。我通过在所有子类中重复“findAll”方法快速解决了这个问题,使用可缓存注释并在每个子类中调用 super.findAll()。
  • 恕我直言,这可能与 java 中的注释不继承这一事实有关。

标签: java spring spring-mvc inheritance caching


【解决方案1】:

这个问题已在another question 中得到解决,它与抽象类无关,更多地与框架确定使用哪个缓存的能力有关。

长话短说(引自Spring documentation),您缺少合适的CacheResolver,它适用于您的抽象类层次结构:

从 Spring 4.1 开始,缓存注解的 value 属性不再是强制性的,因为无论注解的内容如何,​​CacheResolver 都可以提供此特定信息。

因此,您的抽象类应该定义一个缓存解析器,而不是直接声明缓存名称。

abstract class Repository<T> {
    // .. some methods omitted for brevity

    @Cacheable(cacheResolver = CachingConfiguration.CACHE_RESOLVER_NAME)
    public List<T> findAll() {
        return getDao().findAll();
    }
}

解析器确定用于拦截的方法调用的 Cache 实例。一个非常幼稚的实现可以获取目标存储库 bean(按名称)并将其用作缓存名称

class RuntimeCacheResolver
        extends SimpleCacheResolver {

    protected RuntimeCacheResolver(CacheManager cacheManager) {
        super(cacheManager);
    }

    @Override
    protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
        return Arrays.asList(context.getTarget().getClass().getSimpleName());
    }
}

这样的解析器需要明确的配置:

@Configuration
@EnableCaching
class CachingConfiguration extends CachingConfigurerSupport {

    final static String CACHE_RESOLVER_NAME = "simpleCacheResolver";

    @Bean
    @Override
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager();
    }

    @Bean(CACHE_RESOLVER_NAME)
    public CacheResolver cacheResolver(CacheManager cacheManager) {
        return new RuntimeCacheResolver(cacheManager);
    }
}

我有create a Gist,它更详细地描述了整个概念。

免责声明

以上 sn-ps 仅用于演示,旨在提供指导,而不是提供完整的解决方案。上面的缓存解析器实现非常幼稚,并没有考虑很多事情(比如方法参数等)。我永远不会在生产环境中使用它。

Spring 处理缓存的方式是通过代理,其中@Cacheable 注释声明缓存,以及在运行时处理的命名信息。缓存是通过提供给缓存解析器的运行时信息来解析的(毫不奇怪,它与经典 AOP 的 InvocationContext 有一些相似之处)。

public interface CacheOperationInvocationContext<O extends BasicOperation> {
    O getOperation();
    Object getTarget();
    Method getMethod();
    Object[] getArgs();
}

通过getTarget() 方法可以确定代理了哪个bean,但在现实生活中,应该考虑更多信息,以提供可靠的缓存(如方法参数等)。

【讨论】:

  • 嘿@jakub-marchwicki,这是一个后续问题:你知道如何配置动态创建的缓存吗?就像您的建议一样,我使用复杂的逻辑实现了getCacheNames-方法,该逻辑将类名、方法名和参数考虑在内并生成一个名称。我们正在使用 EHCache 并通过ehcache.xml 对其进行配置。对于不同的生成缓存,我需要不同的配置,但是因为名称是动态生成的,所以我不能把它放在ehcache.xml
  • 很遗憾,我没有使用 EhCache 的经验。我一直在使用内存缓存,所以无法为您提供帮助。也许有一种方法可以以编程方式配置 EhCache(虽然是 bean 而不是 xml)——但我从来没有这样做过
  • 很好的解决方案!完全解决了我遇到的问题。
  • 这不起作用,因为您使用简单名称保存缓存名称。如果有人想在一些演示项目中使用这个解决方案......所以至少将 getCacheNames 替换为“return Arrays.asList(context.getTarget().getClass().getSimpleName() + context.getMethod());”跨度>
猜你喜欢
  • 1970-01-01
  • 2018-10-26
  • 1970-01-01
  • 1970-01-01
  • 2017-06-20
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 2012-12-02
相关资源
最近更新 更多