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 的执行,然后再进行迁移发生了。
当两个注释都存在时,名为 internalAsyncAnnotationProcessor
的 BeanPostProcessor
的注册发生在 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.
通过此调整,数据库设置再次按预期执行。
我想在我的 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 的执行,然后再进行迁移发生了。
当两个注释都存在时,名为 internalAsyncAnnotationProcessor
的 BeanPostProcessor
的注册发生在 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
:
To customize all this, implement AsyncConfigurer and provide: * your own Executor through the getAsyncExecutor() method, and * your own AsyncUncaughtExceptionHandler through the getAsyncUncaughtExceptionHandler() method.
通过此调整,数据库设置再次按预期执行。