Mat*_*etl 10 java inheritance spring caching spring-mvc
我试图在抽象类中使用Spring Cache但它不起作用,因为从我所看到的,Spring正在抽象类中搜索CacheNames.我有一个REST API,它使用服务层和dao层.我们的想法是为每个子类设置不同的缓存名称.
我的抽象服务类看起来像这样:
@Service
@Transactional
public abstract class AbstractService<E> {
...
@Cacheable
public List<E> findAll() {
return getDao().findAll();
}
}
Run Code Online (Sandbox Code Playgroud)
抽象类的扩展名如下所示:
@Service
@CacheConfig(cacheNames = "textdocuments")
public class TextdocumentsService extends AbstractService<Textdocuments> {
...
}
Run Code Online (Sandbox Code Playgroud)
所以当我用这段代码启动应用程序时,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:?]
Run Code Online (Sandbox Code Playgroud)
我认为这是因为Spring在抽象类上搜索CacheName,尽管它是在子类上声明的.
试着用
@Service
@Transactional
@CacheConfig
public abstract class AbstractService<E> {
}
Run Code Online (Sandbox Code Playgroud)
导致同样的例外; 运用
@Service
@Transactional
@CacheConfig(cacheNames = "abstractservice")
public abstract class AbstractService<E> {
}
Run Code Online (Sandbox Code Playgroud)
没有例外,但Spring Cache为每个子类使用相同的缓存名称,并忽略在子类上定义的缓存名称.有什么想法可以解决这个问题?
这个问题已在另一个问题中得到解决,与抽象类无关,而与框架确定使用哪个缓存的能力有关。
长话短说(引自Spring 文档)您缺少适用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();
}
}
Run Code Online (Sandbox Code Playgroud)
解析器确定用于拦截的方法调用的 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());
}
}
Run Code Online (Sandbox Code Playgroud)
这样的解析器需要显式配置:
@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);
}
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个 Gist,它更详细地描述了整个概念。
免责声明
以上代码片段仅用于演示,旨在提供指导而非提供完整的解决方案。上面的缓存解析器实现非常幼稚,没有考虑很多东西(比如方法参数等)。我永远不会在生产环境中使用它。
Spring 处理缓存的方式是通过代理,其中@Cacheable注解声明缓存,以及在运行时处理的命名信息。缓存通过提供给缓存解析器的运行时信息来解析(毫不奇怪,它与经典 AOP 的 InvocationContext 有一些相似之处)。
public interface CacheOperationInvocationContext<O extends BasicOperation> {
O getOperation();
Object getTarget();
Method getMethod();
Object[] getArgs();
}
Run Code Online (Sandbox Code Playgroud)
通过该getTarget()方法可以确定代理了哪个 bean,但在现实生活中,应该考虑更多信息,以提供可靠的缓存(如方法参数等)。