gpt4 book ai didi

java - Spring Cache 不适用于抽象类

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:01:36 26 4
gpt4 key购买 nike

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

最佳答案

此问题已在 another question 中解决并且与抽象类无关,而与框架确定要使用哪个缓存的能力有关。

长话短说(引自 Spring documentation)您缺少适用于您的抽象类层次结构的 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.

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

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

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

解析器确定用于拦截方法调用的缓存实例。一个非常天真的实现可以获取目标存储库 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其中更详细地描述了整个概念。

免责声明

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

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

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

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

关于java - Spring Cache 不适用于抽象类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36977643/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com