8

与 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();
    }
    

这导致 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() 有问题”

4

3 回答 3

6

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

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

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

于 2015-02-07T14:07:47.537 回答
4

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

@Component
public class MyInitializer implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        //doing things
    }
}
于 2016-03-03T23:45:33.887 回答
0

使用以下方式自动装配ApplicationContext和调用方法调用:

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

getRawBundle方法在哪里Cacheable

于 2019-08-13T13:27:33.793 回答