繁体   English   中英

Spring Cache不适用于抽象类

[英]Spring Cache not working for abstract classes

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

我的抽象服务类看起来像这样:

    @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为每个子类使用相同的缓存名称,并忽略在子类上定义的缓存名称。 有什么想法可以解决这个问题?

这个问题已在另一个问题中得到解决,而不是关于抽象类以及更多关于框架能够确定使用哪个缓存的能力。

长话短说(引用Spring文档 )你缺少适用CacheResolver抽象类层次结构的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);
    }
}

创建了一个Gist ,它更详细地描述了整个概念。

放弃

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM