我有以下方法:
@Cacheable(value = "SAMPLE")
public List<SomeObj> find() {
// Method that initiates and returns the List<SomeObj> and takes around 2-3 seconds, does some logging too
}
我正在我的一个配置类中启用缓存:
@EnableCaching
@Configuration
public SomeConf extends CachingConfigurerSupport {
// Here I also initialize my classes with @Cacheable annotation
@Bean
@Override
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList((new ConcurrentMapCache("SAMPLE"))));
return cacheManager;
}
@Bean
@Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager());
}
@Bean
@Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
}
我的pom.xml
中有以下内容:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>1.5.14.RELEASE</version>
</dependency>
我声明CacheManager
如下:
@Bean
public CacheManager cacheManager(){
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList((new ConcurrentMapCache("SAMPLE"))));
return cacheManager;
}
当我将@Autowired
CacheManager
个实例放入其中一个@Service
实例中时,我可以看到存在一个名为"SAMPLE"
的缓存,但其条目始终为空。我一次又一次地调用方法find()
,但是它似乎并未填充缓存。
我尝试将一个参数(例如int a
)放置到find()
方法中,并将其作为key = "#a"
放置到@Cacheable
中,但是没有改变。
当我尝试在孤立的环境中重新创建问题时,可以看到它正常运行。但是,当我添加依赖项时(非开源公司的库,其中也包含EhCache
配置),它不起作用。我该如何调试,我在做什么错了?
更新:
我也尝试在cacheManager = myCacheManager
中使用@Cacheable
。没有运气。
更新2:
我正在使用AspectJ
和Spring AOP。我认为这可能与它有关。我已经尝试将@EnableCaching(mode = AdviceMode.ASPECTJ)
与@EnableLoadTimeWeaving
一起使用,但是还是一样。
更新3:
我终于能够重现该问题,这里是:client-api-cache
基本上,当您运行该应用程序并向其发送任何行后telnet localhost 9000
运行时,即使该方法在NOT CACHED
中被调用了两次,它也应该打印一次CachedController
(第二个来自缓存)。但是它会打印两次。
答案 0 :(得分:3)
根本原因是您滥用了“ afterPropertiesSet”。因此,您正在做的是无限循环,并且永远不会将控制权传递回Spring管道,因此Spring无法正确初始化缓存工具。
查看可解决问题的代码:https://dumpz.org/cbx8h28KeAss
答案 1 :(得分:1)
正如Andrews S所指出的,这听起来像是在自动配置中碰撞缓存。
@EnableCaching
's javadoc有一些有关如何选择cacheManager
的有用注释,尤其是关于如何进行操作的想法。在这种情况下,您要做的是建立一个CachingConfigurer
以选择您的缓存-也许您只需扩展CachingConfigurerSupport
(如下例所示)即可。
必须注册类型
CacheManager
的bean,因为没有合理的默认值可以将该框架用作约定。并且<cache:annotation-driven>
元素假定一个名为“ cacheManager”的bean,而@EnableCaching
按类型搜索一个缓存管理器bean。因此,缓存管理器bean方法的命名并不重要。对于那些希望在
@EnableCaching
和要使用的确切缓存管理器bean之间建立更直接关系的用户,可以实现CachingConfigurer
回调接口。请注意以下@Override
注释的方法:
@Configuration
@EnableCaching
public class AppConfig extends CachingConfigurerSupport {
@Bean
public MyService myService() {
// configure and return a class having @Cacheable methods
return new MyService();
}
@Bean
@Override
public CacheManager cacheManager() {
// configure and return an implementation of Spring's CacheManager SPI
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
return cacheManager;
}
@Bean
@Override
public KeyGenerator keyGenerator() {
// configure and return an implementation of Spring's KeyGenerator SPI
return new MyKeyGenerator();
}
}
仅因为它更明确,可能需要此方法,或者可能需要此方法以区分存在于同一容器中的两个
CacheManager
bean。还要注意上面示例中的
keyGenerator
方法。这允许根据Spring的KeyGenerator
SPI自定义用于生成缓存密钥的策略。通常,@EnableCaching
为此将配置Spring的SimpleKeyGenerator
,但是在实现CachingConfigurer
时,必须显式提供密钥生成器。如果不需要自定义,则从此方法返回null
或new SimpleKeyGenerator()
。
CachingConfigurer
提供了附加的自定义选项:建议从CachingConfigurerSupport
扩展,为所有方法提供默认实现,如果不需要自定义所有内容,这将很有用。有关更多详细信息,请参见CachingConfigurer
Javadoc。
您可能还会发现此线程很有用:How to have multiple cache manager configuration in spring cache java
编辑:
因此,值得庆幸的是,我有一个使用缓存的项目并编写了以下内容:
@Bean
@Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager()) {
@Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
Collection<String> toReturn = super.getCacheNames(context);
toReturn.forEach(System.out::println);
return toReturn;
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
System.out.println(Arrays.toString(context.getArgs()));
System.out.println(context.getClass());
System.out.println(context.getMethod());
System.out.println(context.getOperation());
return super.resolveCaches(context);
}
};
}
除了看到我建立的缓存名称弹出,我还注意到上下文正在输出:
[]
org.springframework.cache.interceptor.CacheAspectSupport $ CacheOperationContext类 公共摘要... transferobjects.CacheContainer ... service.LookupService.getCacheSelections()
Builder [public ... transferobjects.CacheContainer ... dao.LookupFacade.getCacheSelections()] caches = [cacheselections] |键=''| keyGenerator =''| | cacheManager =''| | cacheResolver =''|条件=''|除非=''| sync ='true'
考虑到您的纵横比问题,context.getClass()
的输出很有趣。就是说,我在自己的代码中有一个非常类似的日志记录/计时方面,不会对其余的缓存造成任何混乱。试用我的解析器,看看输出是否有启发性,说明对缓存代码进行的调用。
编辑#2:
TLDR问题似乎是我们期望@Cacheable
在无法运行的地方工作-主要是由于该框架尚未完全完善。您正在使用InitializingBean
,但我尝试用@PostConstruct
替换该功能,但都失败了。
Spring cache using @Cacheable during @PostConstruct does not work
我得到了您的github代码。最初,我遇到了您在另一个线程中报告的阻塞问题,但是为了一次执行一件事,我只是注释掉了该阻塞代码并直接调用了service.cachedMethod()
。
我使用--trace运行您的jar文件,并注意到已扫描并创建了cacheManager,但最初对它的调用对我来说并不明显。因此,没什么大不了的,我只是让bean方法抛出了RuntimeException
。完成此操作后,我注意到您的NOT CACHED
行都在该调用之前打印-另一个提示是未按预期进行设置。
最后,我在控制器的afterPropertiesSet()方法中添加了System.out.println(service.toString());
,并看到了com.company.client.api.domain.CachedServiceImpl@2bbfb8b
-您的对象,而不是代理对象。因此无需缓存。
因此,通常,您需要重新尝试尝试启用此功能的方式。
答案 2 :(得分:-1)
确保按以下方式设置缓存到cachemanager中。
@EnableCaching
@Configuration
public SomeConf {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("find():List<SomeObj>")));
return cacheManager;
}
}