简体   繁体   English

Spring Cache不适用于抽象类

[英]Spring Cache not working for abstract classes

I'm trying to use Spring Cache within abstract classes but it won't work, because, from what I can see, Spring is searching for CacheNames on the abstract class. 我试图在抽象类中使用Spring Cache但它不起作用,因为从我所看到的,Spring正在抽象类中搜索CacheNames。 I'm having a REST API which uses a service layer and a dao layer. 我有一个REST API,它使用服务层和dao层。 The idea is to have a different cache name for every subclass. 我们的想法是为每个子类设置不同的缓存名称。

My abstract service class looks like this: 我的抽象服务类看起来像这样:

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

...

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

An extension of the abstract class would look like this: 抽象类的扩展名如下所示:

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

So when I start the application with this code, Spring gives me the following exception: 所以当我用这段代码启动应用程序时,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:?]

I think this happens because Spring is searching for the CacheName on the abstract class, despite it is being declared on the subclass. 我认为这是因为Spring在抽象类上搜索CacheName,尽管它是在子类上声明的。

Trying to use 试着用

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

leads to the same exception; 导致同样的例外; using 运用

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

gives no exception, but then Spring Cache uses the same cache name for every subclass and ignores the cache name defined on the subclass. 没有例外,但Spring Cache为每个子类使用相同的缓存名称,并忽略在子类上定义的缓存名称。 Any Ideas to so solve this? 有什么想法可以解决这个问题?

This problem has been addressed in another question and is less about abstract classes and more about the framework's ability to figure out which cache to use. 这个问题已在另一个问题中得到解决,而不是关于抽象类以及更多关于框架能够确定使用哪个缓存的能力。

Long story short (quoting from Spring documentation ) you are missing appropriate CacheResolver that will work with your abstract class hierarchy: 长话短说(引用Spring文档 )你缺少适用CacheResolver抽象类层次结构的CacheResolver

Since Spring 4.1, the value attribute of the cache annotations are no longer mandatory, since this particular information can be provided by the CacheResolver regardless of the content of the annotation. 从Spring 4.1开始,缓存注释的value属性不再是必需的,因为无论注释的内容如何,​​CacheResolver都可以提供此特定信息。

Therefore, your abstract class should define a caching resolver instead of directly stating the cache name. 因此,您的抽象类应该定义缓存解析器,而不是直接声明缓存名称。

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

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

The resolver determines the Cache instance(s) to use for an intercepted method invocation. 解析器确定用于截获的方法调用的Cache实例。 A very naive implementation can take the target repository bean (by name) and use it as the cache name 一个非常天真的实现可以使用目标存储库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());
    }
}

Such resolver needs an explicit configuration: 这样的解析器需要一个明确的配置:

@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);
    }
}

I've create a Gist which describes the whole concept in more details. 创建了一个Gist ,它更详细地描述了整个概念。

Disclaimer 放弃

The above snippets are just for demonstration and are intended to give direction than to provide a complete solution. 以上片段仅用于演示,旨在提供方向而不是提供完整的解决方案。 The above cache resolver implementation is very naive and doesn't consider many things (like method parameters etc.). 上面的缓存解析器实现非常幼稚,并没有考虑很多事情(比如方法参数等)。 I'd never use it in a production environment. 我永远不会在生产环境中使用它。

The way Spring handles caching is through proxies, where the @Cacheable annotation declares the cache, together with naming information processed on runtime. Spring处理缓存的方式是通过代理,其中@Cacheable注释声明缓存,以及在运行时处理的命名信息。 The cache is resolved through runtime information provided to cache resolver (no surprise it resembles some similarities to InvocationContext of classical AOP). 通过提供给缓存解析器的运行时信息来解析缓存(毫不奇怪,它类似于经典AOP的InvocationContext的一些相似之处)。

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

Through the getTarget() method it is possible to figure out which bean is proxied, but in real-life, more information should be taken into account, to provide a reliable cache (like method parameters, etc). 通过getTarget()方法,可以确定代理哪个bean,但在现实生活中,应该考虑更多信息,以提供可靠的缓存(如方法参数等)。

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

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