У меня есть небольшая проблема с методами @Async
которые возвращают void
(или Unit
, я пишу в Kotlin) в моем приложении Spring.
Я не знаю почему, но когда метод @Async
возвращает 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).
Когда я меняю подпись функции, чтобы она возвращала Future<String>
и добавляла фиктивную возвратную строку в конец функции, а затем вызываю service.sendEmail(...).get()
, тело метода магически выполняется и приходит электронная почта.
В моем классе @Configuration
существует @EnableAsync
. Я также реализовал AsyncConfigurer
и предоставил собственный исполнитель и обработчик исключений, потому что я думал, что это могло быть что-то с моим определением bean-компонента исполнителя, но ничего не помогает.
Это сводит меня с ума, потому что я просто хочу тихо выполнить что-то в фоновом режиме, и это не сработает. Я молча говорю, что не хочу, чтобы меня беспокоили исключения, брошенные внутри.
У тебя есть идеи?
Обновление: так как @pleft посоветовал, я поместил некоторые отпечатки внутри своего метода. Теперь, когда я запускаю mvn clean test
, я вижу, что 1,2,3 печатаются, но не каждый раз. Иногда печатались только 1 и 2. Я также помещаю печать в свой AsyncUncaughtExceptionHandler
, но этот вызов не вызывается. Похоже, что фоновый поток убивается слишком рано или еще что-то.
Обновление 2:
Мой ServiceConfig:
@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..
Может быть, это важно, может быть, нет, но я использую Thymeleaf в методе processTemplate.
Основываясь на разговоре в комментарии и моем собственном наблюдении (например, о том, чтобы немного поспать в методе тестирования), я выяснил причину. Это было причиной разрушения контекста весны внутри теста. Поскольку у меня был только один тест, запускающий этот асинхронный метод, поэтому сценарий заключался в том, что метод async возвращался немедленно, а также метод тестирования. Поскольку это был последний (и единственный) метод в моем тестовом наборе, тестовый Контекст был разрушен (а также все созданные им потоки).
Таким образом, разрешение состоит в том, чтобы либо поставить спит в тестах (очень уродливо), либо взглянуть на этот вопрос и получить вдохновение.