Spring @EnableAsync 破坏 bean 初始化顺序?

Spring @EnableAsync breaks bean initialization order?

我想在我的 SpringBoot 应用程序中引入 @Async 方法(用于并行发送邮件)。 但是当我将 @EnableAsync 注释放在我们应用程序的主要 @Configuration class 上(用 @SpringBootApplication 注释)时,Flyway DB 迁移在 DataSourceInitializer 之前执行(这为我的测试运行 schema.sql 和 data.sql)已执行。

第一个涉及 'should-be-migrated' 数据库的操作 table 失败。

删除 @EnableAsync 会使一切恢复正常。为什么会发生这种情况,我该如何解决(或解决该问题)?

Update 更多发现:@EnableAsync(mode = AdviceMode.ASPECTJ) 保留数据库设置的原始顺序,但 @Async 方法与调用者线程在同一线程上运行.我还看到 Bean 'objectPostProcessor' 是在 @EnableAsync 不存在或使用 @EnableAsync(mode = AdviceMode.ASPECTJ) 时早期创建的(第 3 个 bean)。当仅使用 @EnableAsync 时,此 bean 的创建时间要晚得多。

更新 2 虽然我还不能创建一个重现问题的最小项目,但我发现在受影响的应用程序中恢复了正确的数据库设置顺序我把下面的@EnableWebSocketMessageBroker注释掉了:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
{ 
  ...
}

如果 @EnableWebSocketMessageBroker 存在,Bean 'webSocketConfig' 是创建的第一个 bean(根据 INFO 级控制台输出)。

事实证明,在我的应用程序中同时存在 @EnableAsync@EnableWebSocketMessageBroker 导致了所描述的效果。

删除其中一个,恢复了预期的行为,在这种情况下,DataSourceInitializerPostProcessor 创建了 DataSourceInitializer,触发了 schema.sql 和 data.sql 的执行,然后再进行迁移发生了。

当两个注释都存在时,名为 internalAsyncAnnotationProcessorBeanPostProcessor 的注册发生在 DataSourceInitializerPostProcessor 注册之前。

问题的原因是 internalAsyncAnnotationProcessor 的注册导致创建 dataSource bean 作为副作用。这种副作用是由于 spring 正在寻找要使用的 TaskExecutor bean,用于 @Async 方法执行。 spring 意外地拾取了由于 @EnableWebSocketMessageBroker 而出现的 clientInboundChannelExecutor bean。使用这个 bean 导致 WebSocketMessagingAutoConfiguration 的实例化,它创建了 objectMapper bean(用于 json-序列化),它使用依赖于 dataSource 的 DAO 存储库的服务。所以所有这些 bean 都被创建了。

因为 DataSourceInitializerPostProcessor 当时甚至还没有注册,所以 DataSourceInitializer 是在 flyway 迁移发生后很久才创建的。

@EnableAsync 的 javadoc 说明如下:

By default, a SimpleAsyncTaskExecutor will be used to process async method invocations. Besides, annotated methods having a void return type cannot transmit any exception back to the caller. By default, such uncaught exceptions are only logged.

我假设,SimpleAsyncTaskExecutor 将被创建到 运行 @Async 方法,但是 spring 选择了一个具有匹配类型的现有 bean。

所以这个问题解决方案是实施AsyncConfigurer,并提供我自己的Executor@EnableAsync:

的 javadoc 中也建议这样做

To customize all this, implement AsyncConfigurer and provide: * your own Executor through the getAsyncExecutor() method, and * your own AsyncUncaughtExceptionHandler through the getAsyncUncaughtExceptionHandler() method.

通过此调整,数据库设置再次按预期执行。