测试 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(), 方法的主体神奇地执行并且电子邮件到达。

我的@Configurationclass里有@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 上下文。因为我只有一个测试 运行 这个异步方法,所以场景是异步方法立即返回,测试方法也是如此。因为它是我测试套件中的最后一个(也是唯一一个)方法,所以测试上下文被销毁(以及它创建的所有线程)。

所以解决方案是要么在测试中加入睡眠(非常难看),要么看看 并获得灵感。