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

时间:2015-02-05 17:15:03

标签: java postconstruct spring-cache

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

中的提交有关

初始化设置为稍后阶段,从 afterPropertiesSet() afterSingletonsInstantiated()

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

更长版本: 这可以防止用于

的用例
  1. 在methodB上使用@Cacheable创建serviceB

  2. 使用@PostConstruct调用serviceB.methodB

    创建serviceA
    @Component 
    public class ServiceA{
    
    @Autowired
    private ServiceB serviceB;
    
    @PostConstruct
    public void init() {
        List<String> list = serviceB.loadSomething();
    }
    
  3. 这导致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()的问题”

3 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:4)

与OP完全相同的问题并且监听ContextRefreshedEvent导致我的初始化方法被调用两次。听ApplicationReadyEvent对我来说效果最好。 这是我使用的代码

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

答案 2 :(得分:0)

自动连接ApplicationContext并使用调用方法调用:

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

其中getRawBundleCacheable方法。