An ExecutorService bean does not call the shutdown method on Java 19 with SpringBoot (2.7.12 or 3.1.0). This results in @SpringBootTest tests never completing.
This only happens when you have a custom ExecutorService bean, implements the SchedulingConfigurer and includes the @Scheduled annotation.
The @Bean JavaDoc states that the destroyMethod is inferred, and shutdown is one of the options.
The following code breaks
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
/**
* Works if @Scheduled is not included.
*/
@Scheduled(cron = "0 0 1 * * *")
protected void schedule() {
}
}
Test never completes.
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(
classes =
{ScheduleConfig.class})
class TestIT {
@Test
void test() {
//NOOP
}
}
A workaround is to explicitly set the destroyMethod like
@Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
This is because jdk 19 start implementing
AutoClosableinterfaceDisposableBeanAdapter will invoke
closeby default for bean implementingAutoClosableReferring to the method close, it will block until terminated, and as your cron expression is set to invoke at 1 o'clock, it will terminate when the scheduled job finished at 1 o'clock, that's why it seems never completed. You may try
* * * * * *and it will finish immediately.Related: Why does the ExecutorService interface not implement AutoCloseable?