@Scheduled任务抛异常致应用关闭,求替代try-catch的优雅处理方案
解决Spring Boot定时任务异常导致应用关闭的优雅方案
Spring Boot中使用@Scheduled注解的定时任务,若抛出未捕获异常,默认会导致整个应用终止。不想在每个任务方法里写全量try-catch的话,可以通过以下几种全局统一的方式处理,仅记录异常而不终止应用:
1. 自定义ThreadPoolTaskScheduler捕获异常
继承ThreadPoolTaskScheduler,重写执行方法,统一包裹任务逻辑并捕获异常:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.stereotype.Component; import java.util.concurrent.Runnable; @Component public class ExceptionHandlingTaskScheduler extends ThreadPoolTaskScheduler { private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlingTaskScheduler.class); @Override public void execute(Runnable task) { super.execute(wrapTaskWithExceptionHandling(task)); } private Runnable wrapTaskWithExceptionHandling(Runnable task) { return () -> { try { task.run(); } catch (Throwable t) { logger.error("定时任务执行出现异常", t); // 仅记录异常,不向上抛出,避免应用关闭 } }; } }
所有通过该Scheduler执行的定时任务都会自动应用异常处理逻辑,无需修改任务代码。
2. 利用@EnableScheduling的errorHandler属性(Spring Boot 2.1+)
从Spring Boot 2.1版本开始,@EnableScheduling支持指定全局异常处理器,实现ScheduledTaskExecutor.ErrorHandler接口即可:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.ScheduledTaskExecutor; import org.springframework.stereotype.Component; @Component("scheduledTaskErrorHandler") public class ScheduledTaskErrorHandler implements ScheduledTaskExecutor.ErrorHandler { private static final Logger logger = LoggerFactory.getLogger(ScheduledTaskErrorHandler.class); @Override public void handleError(Throwable t) { logger.error("定时任务执行异常", t); } }
在启动类上通过注解指定该处理器:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling(errorHandler = "scheduledTaskErrorHandler") public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
这种方式配置简洁,全局生效,所有定时任务的未捕获异常都会被统一处理。
3. 用TaskDecorator包装任务逻辑
给ThreadPoolTaskScheduler配置自定义TaskDecorator,对每个任务进行异常处理包装:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.task.TaskDecorator; import org.springframework.stereotype.Component; import java.util.concurrent.Runnable; @Component public class ExceptionHandlingTaskDecorator implements TaskDecorator { private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlingTaskDecorator.class); @Override public Runnable decorate(Runnable runnable) { return () -> { try { runnable.run(); } catch (Throwable t) { logger.error("定时任务执行异常", t); } }; } }
然后在Scheduler配置类中注入该装饰器:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @Configuration public class SchedulerConfig { @Bean public ThreadPoolTaskScheduler threadPoolTaskScheduler(ExceptionHandlingTaskDecorator decorator) { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setTaskDecorator(decorator); scheduler.setPoolSize(5); // 根据需求配置线程池大小 scheduler.initialize(); return scheduler; } }
以上三种方案都能实现全局统一的定时任务异常处理,避免了重复编写try-catch代码,既能记录异常信息,又能防止异常导致应用关闭。
内容的提问来源于stack exchange,提问作者AAAA
相关产品推荐
相关产品推荐

