【问题标题】:Spring cache using @Cacheable during @PostConstruct does not work在 @PostConstruct 期间使用 @Cacheable 的 Spring 缓存不起作用
【发布时间】:2015-04-05 16:03:46
【问题描述】:

与spring框架https://github.com/spring-projects/spring-framework/commit/5aefcc802ef05abc51bbfbeb4a78b3032ff9eee3中的commit相关

初始化设置为从 afterPropertiesSet()afterSingletonsInstantiated() 的后期阶段

简而言之: 这可以防止缓存在 @PostConstruct 用例中使用时起作用。

加长版: 这可以防止您使用的用例

  1. 在方法 B 上使用 @Cacheable 创建 serviceB

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

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

这导致 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();
}

我的解决方法是手动调用初始化方法:

@Configuration
public class SomeConfigClass{

  @Inject
  private CacheInterceptor cacheInterceptor;

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

这当然解决了我的问题,但除了被调用 2 次(1 次手动调用和 1 次按预期由框架调用)之外,它是否有副作用

我的问题是: “这是一个安全的解决方法吗,因为最初的提交者似乎在使用 afterPropertiesSet() 时遇到了问题”

【问题讨论】:

  • @PostConstruct 不保证代理已经创建(这就是为什么@Transactional 不适用于@PostConstruct 方法的原因。@PostConstruct 方法在之后调用构造和注入依赖项之后,但几乎总是在创建代理之前。为什么在@PostConstruct方法中需要它?通常ApplicationListener&lt;ContextRefreshedEvent&gt;比实现SmartInitializingSingleton接口更好,而不是@987654333 @.
  • 感谢您的回复。我们使用 postconstruct 使用从另一个服务(其方法上有 @Cacheable )获取的值来初始化 bean 我们希望即使使用 postconstruct 也会缓存这些值。如果不是这种情况,那么 java 文档将使框架受益,因为其他开发人员可能也不知道这一点。将改为尝试 SmartInitializingSingleton。谢谢!
  • 这在the reference guide 中进行了解释,请阅读该部分的最后一段。

标签: java postconstruct spring-cache


【解决方案1】:

正如 Marten 已经说过的,您不应该在 PostConstruct 阶段使用任何这些服务,因为您无法保证代理拦截器此时已完全启动。

预加载缓存的最佳方法是收听ContextRefreshedEvent(4.2 中提供更多支持)并在那里完成工作。话虽如此,我知道可能不清楚这种用法是否被禁止,因此我创建了SPR-12700 来改进文档。我不确定您指的是什么 javadoc。

回答您的问题:不,这不是一个安全的解决方法。您之前使用的“副作用”(即它不应该工作,如果您的 bean 在 CacheInterceptor 之前初始化,那么旧版本的框架也会遇到同样的问题)。不要在自己的代码中调用这样的低级基础设施。

【讨论】:

    【解决方案2】:

    刚刚遇到与 OP 完全相同的问题,并且收听 ContextRefreshedEvent 导致我的初始化方法被调用了两次。听ApplicationReadyEvent 最适合我。 这是我使用的代码

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

    【讨论】:

      【解决方案3】:

      Autowire ApplicationContext 并使用以下方法调用方法:

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

      其中getRawBundleCacheable 方法。

      【讨论】:

        猜你喜欢
        • 2017-06-01
        • 2018-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-02
        • 1970-01-01
        • 2020-05-03
        相关资源
        最近更新 更多