为什么在Spring上下文初始化期间添加Spring AOP方面会破坏此异步调用?

时间:2017-12-09 22:23:56

标签: java spring concurrency spring-aop

我很难理解手头的问题,我认为这是Spring代理创建方式的问题。

在这个最小的例子中,我有两个类AccountLoaderBankImpl,它们实现了一个接口Bank。启动时,AccountLoader会对自动装配的Bank - 实例执行一些并发调用,其中BankImpl中的方法建议使用方面。

在此设置中,完成未来的调用(Future.get)以TimeoutException结束,因为呼叫似乎永远不会终止。但是,如果在将callables提交给执行程序之前调用相同的方法,则所有调用都会成功完成。

Spring在这发生了什么?为什么这个异步调用没有终止?如果我在异步之前添加同步调用,为什么在所有七个地狱中都会终止呢?

您可以找到以下代码,完整的工作示例也是available on Github

public interface Bank {

    Map<String, String> getAccounts(String q);

}

简单的实现

@Service
public class BankImpl implements Bank {

    private static final Logger LOGGER = LoggerFactory.getLogger(BankImpl.class);

    @Override
    public Map<String, String> getAccounts(String q) {
        LOGGER.info("Listing accounts for {}", q);
        return Collections.singletonMap(q, "q");
    }

}

最后来电者

@Service
public class AccountLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(AccountLoader.class);
    private final ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Autowired
    private Bank bank;

    @PostConstruct
    public void refresh() {
        LOGGER.info("Refreshing accounts");
        // Uncommenting the following line will let the calls terminate
        // bank.getAccounts("sync");
        try {
            executorService.submit(() -> { bank.getAccounts("async"); })
                .get(5L, TimeUnit.SECONDS);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

}

为了完整起见,这是方面

@Aspect
@Component
public class SomeAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(SomeAspect.class);

    @AfterReturning(pointcut = "execution(* com.github.mtritschler.aspects.BankImpl.getAccounts(..))", returning = "returnValue")
    public Map<String, String> logCallee(Map<String, String> returnValue) {
        LOGGER.info("Result is {}", returnValue);
        return returnValue;
    }

}

以及最后但并非最不重要的配置

@EnableAspectJAutoProxy
@Configuration
public class MyConfig {
}

更新:如果我删除@EnableAspextJAutoProxy,我也不会得到例外。切换到加载时编织也没有改变任何东西。

1 个答案:

答案 0 :(得分:0)

事实证明,主线程中的应用程序初始化与注入依赖项的并发访问之间存在竞争条件。

我们在@PostConstruct上为监听器切换ContextRefreshedEvent后,工作得很好。