Spring @Async方法没有捕获/重新抛出异常的问题

时间:2014-06-20 16:54:53

标签: spring asynchronous

我遇到了一个问题,因为我启用了将同步方法转换为异步方法:如果从方法体内抛出异常,我就不能再重新抛出它了。

让我先展示代码:

我的异步/任务执行器配置:

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

我的异步方法:

@Async
@Override
public void sendPasswordResetInfo(String email) {
    Assert.hasText(email);
    Member member = memberRepository.findByEmail(email);
    try {
        mailerService.doMailPasswordResetInfo(member);//EXCEPTION THROWN HERE
    } catch (MessagingException | MailSendException e) {
        log.error("MessagingException | MailSendException", e);
        // TODO: not thrown since @Async is used
        throw new MailerException("MessagingException | MailSendException");//NOT CALLED
    }
}

我在mailerService.doMailPasswordResetInfo引发异常时在控制台中看到的是以下堆栈跟踪:

2014-06-20 18:46:29,249 [ThreadPoolTaskExecutor-1] ERROR com.bignibou.service.preference.PreferenceServiceImpl - MessagingException | MailSendException
org.springframework.mail.MailSendException: Failed messages: javax.mail.SendFailedException: Invalid Addresses;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 Adresse d au moins un destinataire invalide. Invalid recipient. OFR204_418 [418]
; message exception details (1) are:
Failed message 1:
javax.mail.SendFailedException: Invalid Addresses;
  nested exception is:
    com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 Adresse d au moins un destinataire invalide. Invalid recipient. OFR204_418 [418]

    at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:1294)
    at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:635)
    at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:424)
    at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:346)
    at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:341)
    at com.bignibou.service.mailer.MailerServiceImpl.doMailPasswordResetInfo(MailerServiceImpl.java:82)
    at com.bignibou.service.preference.PreferenceServiceImpl.sendPasswordResetInfo_aroundBody10(PreferenceServiceImpl.java:112)
    at com.bignibou.service.preference.PreferenceServiceImpl$AjcClosure11.run(PreferenceServiceImpl.java:1)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:59)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:65)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:63)
    at com.bignibou.service.preference.PreferenceServiceImpl.sendPasswordResetInfo_aroundBody12(PreferenceServiceImpl.java:108)
    at com.bignibou.service.preference.PreferenceServiceImpl$AjcClosure13.run(PreferenceServiceImpl.java:1)
    at org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect.ajc$around$org_springframework_scheduling_aspectj_AbstractAsyncExecutionAspect$1$6c004c3eproceed(AbstractAsyncExecutionAspect.aj:58)
    at org.springframework.scheduling.aspectj.AbstractAsyncExecutionAspect.ajc$around$org_springframework_scheduling_aspectj_AbstractAsyncExecutionAspect$1$6c004c3e(AbstractAsyncExecutionAspect.aj:62)
    at com.bignibou.service.preference.PreferenceServiceImpl.sendPasswordResetInfo(PreferenceServiceImpl.java:108)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:97)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.sun.mail.smtp.SMTPAddressFailedException: 550 5.1.1 Adresse d au moins un destinataire invalide. Invalid recipient. OFR204_418 [418]

    at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:1145)
    ... 28 more

仅供参考,MailerException是我在应用中使用的自定义异常。真正让我感到震惊的是,这个MailerException例外似乎没有被抓住也没有被重新抛出......

有人可以帮忙吗?

编辑1:我修改了我的异步服务方法如下:

@Async
    @Override
    public Future<Void> sendPasswordResetInfo(String email) {
        Assert.hasText(email);
        Member member = memberRepository.findByEmail(email);
        try {
            mailerService.doMailPasswordResetInfo(member);
            return new AsyncResult<Void>(null);
        } catch (MessagingException | MailSendException e) {
            log.error("MessagingException | MailSendException", e);
            //HERE: HOW DO I SEND THE MailerException USING THE FUTURE??
            throw new MailerException("MessagingException | MailSendException");
        }
    }

但是,我不确定如何使用上面的catch块中的AsyncResult 将MailerException发送到Web层......

3 个答案:

答案 0 :(得分:11)

您的日志显示

2014-06-20 18:46:29,249 [ThreadPoolTaskExecutor-1] ERROR com.bignibou.service.preference.PreferenceServiceImpl - MessagingException | MailSendException

记录的

log.error("MessagingException | MailSendException", e);

然后抛出的Exception

throw new MailerException("MessagingException | MailSendException");//NOT CALLED

被基础Thread执行的ThreadPoolTaskExecutor捕获。此代码以异步方式运行,因此异常将在调用该方法的线程中抛出另一个线程。

someCode();
yourProxy.sendPasswordResetInfo(someValue()); // exception thrown in other thread
moreCode();

如果您使方法返回Future,如图所示here,您将能够在其上调用get(),这将重新抛出{{1}内引发的任何异常方法,包含在@Async

答案 1 :(得分:0)

点击此链接http://www.baeldung.com/spring-async,您应该创建:

  1. 实现AsyncUncaughtExceptionHandler的CustomAsyncExceptionHandler

    @Slf4j
    public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
      @Override
      public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
        log.error("**********************************************");
        log.error("Exception message - " + throwable.getMessage());
        log.error("Method name - " + method.getName());
        for (Object param : obj) {
            log.error("Parameter value - " + param);
        }
        log.error("**********************************************");
      }
    }
    
  2. 实现AsyncConfigurer的SpringAsyncConfig

    @Configuration
    @EnableAsync
    public class SpringAsyncConfig implements AsyncConfigurer {
    
      @Override
      public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
      }
    
      @Override
      public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
      }
    }
    

答案 2 :(得分:0)

我创建了一个扩展AsyncConfigurerSupport的配置类,从而解决了我的解决方案,因为上述解决方案在我的实现中不起作用。在此配置类中,我必须定义SimpleAsyncTaskExecutor和一个异步异常处理程序。在处理程序内部,我检查了可抛出实例,并根据其值实现了所需的行为:

if ( throwable instanceOf CustomException) {
// TODO add code
} else {
// TODO
}

参考:Effective advice on spring async exception handler