javaspringspring-boot

Exception @Scheduled Tasks shutdowns Application. @ExceptionHandler or cleaner solution possible?


I have a simple Scheduled Task:

@Component
public class ScheduledTasks {

    @Scheduled
    public void doSomething() {
        ..
    }
}

From there exceptions are propagated to SpringBootApplication.java and if one exception occurs, the whole application is shutdowning.

A clean handling is possible for RestController with @ExceptionHandler.

Is there a cleaner way to prevent shutdown the application than try-catch all exceptions in the doSomething() method?

I'd like to log exceptions, I don't think I can handle them.


Solution

  • Spring's default scheduler uses a single-threaded executor, and unhandled exceptions can kill that thread. Once the thread dies, the @Scheduled task stops running entirely and If it was the only running thread, and Spring didn’t have anything else to do, it may shut down the application context.

    If you don't want to do try catch and still want the app to run you can override Spring Boot’s default scheduler by defining our own and customize the error handler to print the exception.

    Just add the below code which will override the scheduler.

    SchedulerConfig.java

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
    import org.springframework.scheduling.TaskScheduler;
    
    @Configuration
    public class SchedulerConfig {
    
        @Bean
        public TaskScheduler taskScheduler() {
            ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
            scheduler.setPoolSize(1); 
            scheduler.setThreadNamePrefix("scheduled-task-");
    
            //custom error handler
            scheduler.setErrorHandler(t -> {
                System.err.println("[ERROR HANDLER] Scheduled task exception: " + t.getMessage());
                t.printStackTrace();
            });
    
            return scheduler;
        }
    }
    

    Now, you can do whatever you like in your doSomething() method and it won't stop and you will still be able to see the exceptions in the output. (You may add logger instead of sysout)

    @Component
    public class ScheduledTasks {
    
        @Scheduled
        public void doSomething() {
            
            //No need of try catch here now
            // For eg: put int x= 3/0; here which throws ArithmeticException but still your application will be running
        }
    }
    

    You may need to take care of silent failures and this doesn’t work for @Async or CompletableFuture threads as they need AsyncUncaughtExceptionHandler.