代码之家  ›  专栏  ›  技术社区  ›  Hasan Can Saral

@Cacheable不拦截方法,缓存总是空的

  •  2
  • Hasan Can Saral  · 技术社区  · 7 年前

    我有一个方法如下:

    @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 我的一个 @Service 我可以看到存在一个名为 "SAMPLE" ,但其条目始终为空。我一次又一次地调用这个方法 find()

    我试图提出一个论点 int a )到 查找() 方法并将其作为 key = "#a" @Cacheable ,但一切都没有改变。

    当我尝试在一个孤立的环境中重新创建问题时,我可以看到它正常工作。但是当我添加依赖项(非开源公司库,其中包括 EhCache 配置)它不工作。如何调试这个,我做错了什么?

    我也试过使用 cacheManager = myCacheManager @可缓存的 也。运气不好。

    更新2:

    AspectJ @EnableCaching(mode = AdviceMode.ASPECTJ) 具有 @EnableLoadTimeWeaving

    更新3:

    我终于重现了这个问题,现在是: client-api-cache

    telnet localhost 9000 当你发送任何一行到它,它应该打印 NOT CACHED 一次,即使在中调用了两次该方法 CachedController

    3 回复  |  直到 7 年前
        1
  •  3
  •   Beastmaster    7 年前

    根本原因是您误用了“afterPropertiesSet”。所以,您所做的是无休止的循环,并且从不将控制传递回Spring管道,因此Spring无法正确初始化缓存设施。

    https://dumpz.org/cbx8h28KeAss

        2
  •  1
  •   Jon Sampson    7 年前

    正如Andrews S所指出的,这听起来确实像是自动配置中的缓存冲突。

    @EnableCaching 's javadoc 有一些关于 cacheManager 是被选中的,特别是一个关于如何继续的想法。在这种情况下,你要做的是建立一个 CachingConfigurer 选择缓存-也许您可以扩展 CachingConfigurerSupport (如下面的例子)并完成。

    类型的豆子 CacheManager 必须注册,因为框架没有合理的默认值可以用作约定。鉴于 <cache:annotation-driven> 按类型搜索缓存管理器bean。因此,缓存管理器bean方法的命名并不重要。

    对于那些希望在 @启用缓存 以及要使用的缓存管理器bean @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();
         }
    }
    

    这种方法可能仅仅是因为更为明确而可取,也可能是区分两者所必需的 缓存管理器 同一容器中的豆子。

    keyGenerator KeyGenerator SPI公司。正常情况下, @启用缓存 将配置Spring的 SimpleKeyGenerator 大笑配置器 ,必须显式提供密钥生成器。返回 null new SimpleKeyGenerator() 如果不需要自定义,则使用此方法。

    大笑配置器 cachingconfigurers支持 它为所有方法提供了一个默认实现,如果您不需要自定义所有方法,那么这些方法将非常有用。看到了吗 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);
            }
        };
    }
    

    除了看到已建立的缓存名称弹出外,我注意到上下文正在输出:

    []

    公共摘要…transferobjects.CacheContainer…service.LookupService.getCacheSelections()

    生成器[public…transferobjects.CacheContainer…dao.LookupFacade.getCacheSelections()]缓存=[cacheselections]| key=“”| keyGenerator=“”| cacheManager=“”| cacheResolver=“”| condition=“”|除非=“”| sync=“”true'

    这个 context.getClass() 考虑到您的方面问题,输出是有意义的。也就是说,我在自己的代码中有一个非常类似的日志记录/计时方面,这不会对缓存的其余部分造成任何混乱。试试我的解析器,看看输出是否对缓存代码发出的调用有指导意义。

    编辑#2:

    @Cacheable 在它不能工作的地方工作——主要是因为这个框架还没有完全建立起来。您正在使用 InitializingBean 我试着用 @PostConstruct 两者都失败了。

    Spring cache using @Cacheable during @PostConstruct does not work

    我拿到你的github代码了。我最初遇到了您在另一个线程中报告的阻塞问题,但是为了一次处理一件事情,我只是注释掉了阻塞代码并调用了 service.cachedMethod() 直接。

    RuntimeException . 我注意到你的 NOT CACHED

    System.out.println(service.toString()); 在控制器的afterPropertiesSet()方法中 com.company.client.api.domain.CachedServiceImpl@2bbfb8b

    因此,一般来说,您需要重新考虑如何启用此功能。

        3
  •  -1
  •   Sundararaj Govindasamy    7 年前

    设置 将您的缓存保存到cachemanager中,如下所示。

    @EnableCaching
    @Configuration
    public SomeConf {   
    
        @Bean
        public CacheManager cacheManager() {
            SimpleCacheManager cacheManager = new SimpleCacheManager();
            cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("find():List<SomeObj>")));
            return cacheManager;
        }
    
    
    }