测试 Spring 的 @Async 无效返回方法
Testing Spring's @Async void-returning methods
我的 Spring return void
(或 Unit
,我用 Kotlin 编写)的 @Async
方法有点问题应用
我不知道为什么,但是当 @Async
方法 returns void
它只是不执行,或者至少它不执行它应该的到。需要说明的是,在我的异步方法中,我想使用 JavaMailSender
发送电子邮件,所以没什么大不了的。方法如下:
@Async
override fun sendEmail(locale: Locale, subject: String, message: String, to: String) {
val msg = sender.createMimeMessage()
println("1")
val helper = MimeMessageHelper(msg, true, "utf-8")
helper.setFrom("email@example.com")
helper.setTo(to)
println("2")
helper.setSubject(getSubject(locale, null))
println("3")
helper.setText(processTemplate(locale, null, null), true)
println("4")
sender.send(msg)
println("5")
}
但是从来没有收到电子邮件,也没有记录异常(我正在 运行进行 testNG 测试)。
当我更改函数的签名使其成为 return Future<String>
并在函数末尾添加一些虚拟 return 行然后我调用 service.sendEmail(...).get()
, 方法的主体神奇地执行并且电子邮件到达。
我的@Configuration
class里有@EnableAsync
。我还实现了 AsyncConfigurer
并提供了自己的执行程序和异常处理程序,因为我认为这可能与我的执行程序 bean 定义有关,但没有任何帮助。
这快把我逼疯了,因为我只想在后台默默地执行一些东西,但它不起作用。默默地我的意思是我不想被内部抛出的异常所困扰。
你有什么想法吗?
更新:
所以正如@pleft 建议的那样,我在我的方法中放了一些打印件。
现在,当我 运行 mvn clean test
时,我可以看到打印了 1,2,3, 但 不是每次都打印。有时只打印 1 和 2。我还在我的 AsyncUncaughtExceptionHandler
中放置了 print,但是那个没有被调用。
看起来后台线程被杀死得太快了。
更新二:
我的服务配置:
@Configuration
@EnableScheduling
@EnableAsync
@ComponentScan
class ServiceConfig : ApplicationContextAware, EnvironmentAware, AsyncConfigurer {
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
return AsyncUncaughtExceptionHandler { ex, method, params ->
println("Exception thrown")
}
}
override fun getAsyncExecutor(): Executor {
val executor = ThreadPoolTaskExecutor()
executor.corePoolSize = 2
executor.maxPoolSize = 2
executor.setQueueCapacity(500)
executor.threadNamePrefix = "asyncThread"
executor.initialize()
return executor
}
}
/// other beans definitions..
也许这很重要,也许不重要,但我在 processTemplate
方法中使用了 Thymeleaf。
根据评论中的对话和我自己的观察(比如在测试方法中睡一觉)我找到了原因。这是由于破坏了测试中的 Spring 上下文。因为我只有一个测试 运行 这个异步方法,所以场景是异步方法立即返回,测试方法也是如此。因为它是我测试套件中的最后一个(也是唯一一个)方法,所以测试上下文被销毁(以及它创建的所有线程)。
所以解决方案是要么在测试中加入睡眠(非常难看),要么看看 并获得灵感。
我的 Spring return void
(或 Unit
,我用 Kotlin 编写)的 @Async
方法有点问题应用
我不知道为什么,但是当 @Async
方法 returns void
它只是不执行,或者至少它不执行它应该的到。需要说明的是,在我的异步方法中,我想使用 JavaMailSender
发送电子邮件,所以没什么大不了的。方法如下:
@Async
override fun sendEmail(locale: Locale, subject: String, message: String, to: String) {
val msg = sender.createMimeMessage()
println("1")
val helper = MimeMessageHelper(msg, true, "utf-8")
helper.setFrom("email@example.com")
helper.setTo(to)
println("2")
helper.setSubject(getSubject(locale, null))
println("3")
helper.setText(processTemplate(locale, null, null), true)
println("4")
sender.send(msg)
println("5")
}
但是从来没有收到电子邮件,也没有记录异常(我正在 运行进行 testNG 测试)。
当我更改函数的签名使其成为 return Future<String>
并在函数末尾添加一些虚拟 return 行然后我调用 service.sendEmail(...).get()
, 方法的主体神奇地执行并且电子邮件到达。
我的@Configuration
class里有@EnableAsync
。我还实现了 AsyncConfigurer
并提供了自己的执行程序和异常处理程序,因为我认为这可能与我的执行程序 bean 定义有关,但没有任何帮助。
这快把我逼疯了,因为我只想在后台默默地执行一些东西,但它不起作用。默默地我的意思是我不想被内部抛出的异常所困扰。
你有什么想法吗?
更新:
所以正如@pleft 建议的那样,我在我的方法中放了一些打印件。
现在,当我 运行 mvn clean test
时,我可以看到打印了 1,2,3, 但 不是每次都打印。有时只打印 1 和 2。我还在我的 AsyncUncaughtExceptionHandler
中放置了 print,但是那个没有被调用。
看起来后台线程被杀死得太快了。
更新二:
我的服务配置:
@Configuration
@EnableScheduling
@EnableAsync
@ComponentScan
class ServiceConfig : ApplicationContextAware, EnvironmentAware, AsyncConfigurer {
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
return AsyncUncaughtExceptionHandler { ex, method, params ->
println("Exception thrown")
}
}
override fun getAsyncExecutor(): Executor {
val executor = ThreadPoolTaskExecutor()
executor.corePoolSize = 2
executor.maxPoolSize = 2
executor.setQueueCapacity(500)
executor.threadNamePrefix = "asyncThread"
executor.initialize()
return executor
}
}
/// other beans definitions..
也许这很重要,也许不重要,但我在 processTemplate
方法中使用了 Thymeleaf。
根据评论中的对话和我自己的观察(比如在测试方法中睡一觉)我找到了原因。这是由于破坏了测试中的 Spring 上下文。因为我只有一个测试 运行 这个异步方法,所以场景是异步方法立即返回,测试方法也是如此。因为它是我测试套件中的最后一个(也是唯一一个)方法,所以测试上下文被销毁(以及它创建的所有线程)。
所以解决方案是要么在测试中加入睡眠(非常难看),要么看看