如何在Spring中使用DelegatingSecurityContextScheduledExecutorService配置正常关机

问题描述

我正在尝试使用版本2.3中引入的new options to do graceful shutdown with spring,但是我正努力使计划任务的行为相同。

由于在计划任务执行过程中需要上下文中的有效用户,因此我正在使用DelegatingSecurityContextscheduledexecutorservice来实现此目标。

以下是我的SchedulingConfigurer实现示例:

@Configuration
@EnableScheduling
public class ContextSchedulingConfiguration implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean
    public TaskSchedulerCustomizer taskSchedulerCustomizer() {
        return taskScheduler -> {
            taskScheduler.setAwaitTerminationSeconds(120);
            taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
            taskScheduler.setPoolSize(2);
        };
    }

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskScheduler threadPool = new ThreadPoolTaskScheduler();
        taskSchedulerCustomizer().customize(threadPool);
        threadPool.initialize();
        threadPool.setThreadNamePrefix("XXXXXXXXX");

        SecurityContext schedulerContext = createSchedulerSecurityContext();
        return new DelegatingSecurityContextscheduledexecutorservice(threadPool.getScheduledExecutor(),schedulerContext);

    }

    private SecurityContext createSchedulerSecurityContext() {
        //This is just an example,the actual code makes several changes to the context.
        return SecurityContextHolder.createEmptyContext();
    }

    @Scheduled(initialDelay = 5000,fixedDelay = 15000)
    public void run() throws InterruptedException {
        System.out.println("Started at: " + LocalDateTime.Now().toString());
        long until = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30);
        while (System.currentTimeMillis() < until) {}
        System.out.println("Ended at: " + LocalDateTime.Now().toString());
    }
}

但是当我在任务任务正在运行时发送终止信号时,应用程序不会等待任务。

如果在我的bean taskExecutor中替换最后两行,不带上下文返回ThreadPoolTaskScheduler,则一切正常。仅当我返回DelegatingSecurityContextscheduledexecutorservice时它才起作用。

如何设置taskExecutor的上下文并同时配置为等待任务在关闭时完成?

我已经尝试使用TaskScheduler和TaskExecutor接口的另一种实现方式尝试了此代码的几种变体,但没有成功。

解决方法

对于初学者来说,清理代码并在bean方法中使用正确的返回类型(具体而言),并将两者都公开为bean(将一个标记为@Primary!)。

@Configuration
@EnableScheduling
public class ContextSchedulingConfiguration implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(securitytaskScheduler());
    }

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler= new ThreadPoolTaskScheduler();
        taskScheduler.setAwaitTerminationSeconds(120);
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        taskScheduler.setPoolSize(2);
        taskScheduler.setThreadNamePrefix("XXXXXXXXX");
        return taskScheduler;
    }
    
    @Bean
    @Primary
    public DelegatingSecurityContextScheduledExecutorService securitytaskScheduler() {
        SecurityContext schedulerContext = createSchedulerSecurityContext();
        return new DelegatingSecurityContextScheduledExecutorService(taskScheduler().getScheduledExecutor(),schedulerContext);

   }

    private SecurityContext createSchedulerSecurityContext() {
        //This is just an example,the actual code makes several changes to the context.
        return SecurityContextHolder.createEmptyContext();
    }

    @Scheduled(initialDelay = 5000,fixedDelay = 15000)
    public void run() throws InterruptedException {
        System.out.println("Started at: " + LocalDateTime.now().toString());
        long until = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30);
        while (System.currentTimeMillis() < until) {}
        System.out.println("Ended at: " + LocalDateTime.now().toString());
    }
}

重要的是要尽可能明确您的返回类型。尽早检测到配置类,并检查返回类型以确定要进行的回调。现在ThradPoolTaskSchedulerDisposableBean,而Executor不是,也不会收到回调!!