简体   繁体   English

在@PostConstruct期间使用@Cacheable的Spring缓存不起作用

[英]Spring cache using @Cacheable during @PostConstruct does not work

related to the commit in spring framework https://github.com/spring-projects/spring-framework/commit/5aefcc802ef05abc51bbfbeb4a78b3032ff9eee3 与spring框架中的提交有关https://github.com/spring-projects/spring-framework/commit/5aefcc802ef05abc51bbfbeb4a78b3032ff9eee3

the initialisation is set to a later stage from afterPropertiesSet() to afterSingletonsInstantiated() 初始化设置为从afterPropertiesSet()afterSingletonsInstantiated()的后续阶段

In short : This prevents the caching to work when using it in a @PostConstruct use case. 简而言之 :这可以防止在@PostConstruct用例中使用缓存时缓存工作。

Longer version : This prevents the use case where you would 更长的版本 :这可以防止您使用的用例

  1. create serviceB with @Cacheable on a methodB 在methodB上使用@Cacheable创建serviceB

  2. create serviceA with @PostConstruct calling serviceB.methodB 使用@PostConstruct调用serviceB.methodB创建serviceA

     @Component public class ServiceA{ @Autowired private ServiceB serviceB; @PostConstruct public void init() { List<String> list = serviceB.loadSomething(); } 

This results in org.springframework.cache.interceptor.CacheAspectSupport not being initialised now and thus not caching the result. 这导致org.springframework.cache.interceptor.CacheAspectSupport现在不进行初始化,因此不会缓存结果。

protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
   // check whether aspect is enabled
   // to cope with cases where the AJ is pulled in automatically
   if (this.initialized) {
//>>>>>>>>>>>> NOT Being called
      Class<?> targetClass = getTargetClass(target);
      Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
      if (!CollectionUtils.isEmpty(operations)) {
         return execute(invoker, new CacheOperationContexts(operations, method, args, target, targetClass));
      }
   }
//>>>>>>>>>>>> Being called
   return invoker.invoke();
}

My workaround is to manually call the initialisation method: 我的解决方法是手动调用初始化方法:

@Configuration
public class SomeConfigClass{

  @Inject
  private CacheInterceptor cacheInterceptor;

  @PostConstruct
  public void init() {
    cacheInterceptor.afterSingletonsInstantiated();
  }

This of course fixes my issue but does it have side effects other that just being called 2 times (1 manual and 1 by the framework as intended) 这当然解决了我的问题,但它有副作用,只是被调用2次(1个手册和1个框架按预期)

My question is: "Is this a safe workaround to do as the initial commiter seemed to have an issue with just using the afterPropertiesSet()" 我的问题是:“这是一个安全的解决方法,因为初始提交者似乎只有使用afterPropertiesSet()的问题”

As Marten said already, you are not supposed to use any of those services in the PostConstruct phase because you have no guarantee that the proxy interceptor has fully started at this point. 正如Marten已经说过的那样,你不应该在PostConstruct阶段使用任何这些服务,因为你无法保证代理拦截器已经完全启动了。

Your best shot at pre-loading your cache is to listen for ContextRefreshedEvent (more support coming in 4.2) and do the work there. 预加载缓存的最佳方法是监听ContextRefreshedEvent (4.2中的更多支持)并在那里完成工作。 That being said, I understand that it may not be clear that such usage is forbidden so I've created SPR-12700 to improve the documentation. 话虽如此,我知道可能不清楚这种用法是否被禁止,所以我创建了SPR-12700来改进文档。 I am not sure what javadoc you were referring to. 我不确定你指的是什么javadoc。

To answer your question: no it's not a safe workaround. 回答你的问题:不,这不是一个安全的解决方法。 What you were using before worked by "side-effect" (ie it wasn't supposed to work, if your bean was initialized before the CacheInterceptor you would have the same problem with an older version of the framework). 你之前使用的是“副作用”(即它不应该工作,如果你的bean在CacheInterceptor之前被初始化,你会CacheInterceptor与旧版本框架相同的问题)。 Don't call such low-level infrastructure in your own code. 不要在自己的代码中调用这样的低级基础结构。

Just had the exact same problem as OP and listening to ContextRefreshedEvent was causing my initialization method to be called twice. 刚刚遇到与OP完全相同的问题,并且监听ContextRefreshedEvent导致我的初始化方法被调用两次。 Listening to ApplicationReadyEvent worked best for me. ApplicationReadyEvent为我工作最好的。 Here is the code I used 这是我使用的代码

@Component
public class MyInitializer implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        //doing things
    }
}

Autowire ApplicationContext and invoke method call using : Autowire ApplicationContext并使用以下方法调用方法调用:

applicationContext.getBean(RBService.class).getRawBundle(bundleName, DEFAULT_REQUEST_LANG);

where getRawBundle is Cacheable method. 其中getRawBundleCacheable方法。

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

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