Springboot开启定时任务Spring Schedule(含业务场景代码)

Springboot开启定时任务Spring Schedule(内涵业务场景代码)

一、简介

Spring Schedule是Spring框架提供的一种轻量级的任务调度框架,可以用来执行定时任务和周期性任务。在很多应用场景中,我们需要定时执行某些任务,比如定时备份数据库、定时清理缓存、定时发送邮件等等。Spring Schedule提供了很方便的任务调度解决方案,可以很容易地实现这些定时任务。

Spring Boot是Spring框架的一个子项目,它提供了很多开箱即用的特性,可以帮助我们更快速、更便捷地开发Spring应用。在Spring Boot应用中整合Spring Schedule也非常简单,本文将介绍如何在Spring Boot中整合Spring Schedule。

二、添加依赖

引入springboot依赖

三、配置定时任务

在Spring Boot中配置定时任务有两种方式:基于注解和基于XML配置。我们这里选择基于注解的方式。

首先,在启动类上添加@EnableScheduling注解,启用Spring Schedule:

@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

然后,在需要执行定时任务的方法上添加@Scheduled注解,指定定时任务的执行时间:

@Service
public class MyService {
    @Scheduled(cron = "0 0 0 * * ?")
    public void backupDatabase() {
        // 备份数据库的代码
    }
}

这个例子中,我们定义了一个名为backupDatabase的方法,并且使用@Scheduled注解指定了它每天0点执行一次

其中,cron参数是一个Cron表达式,用来指定任务的执行时间。

四、任务并发执行问题

默认情况下,Spring Schedule是单线程执行任务的,也就是说,如果一个任务还没执行完,下一个任务就必须等待。如果任务的执行时间较长,那么会影响其他任务的执行,甚至会导致任务积压,最终导致整个应用崩溃。

为了避免这种情况的发生,我们需要在配置文件中添加以下配置:

spring.task.scheduling.pool.size=10

一个大小为10的线程池来执行任务,也就是说,最多可以并发执行10个任务。这样就能避免任务积压和应用崩溃的问题了。

五、动态调整定时任务

有时候,我们需要动态调整定时任务的执行时间,比如说,某个任务需要紧急执行,我们就需要把它的执行时间提前。在Spring Boot中,我们可以使用@Scheduled注解的fixedRate和fixedDelay属性来实现动态调整定时任务。

fixedRate属性指定任务执行的频率,单位是毫秒。如果任务的执行时间大于fixedRate,那么任务会在执行完后立即再次执行;如果任务的执行时间小于fixedRate,那么任务会在fixedRate时间间隔后再次执行。

fixedDelay属性指定任务执行的延迟时间,单位也是毫秒。如果任务的执行时间大于fixedDelay,那么任务会在执行完后延迟fixedDelay时间后再次执行;如果任务的执行时间小于fixedDelay,那么任务会在fixedDelay-executionTime时间间隔后再次执行。

下面是一个动态调整定时任务执行时间的例子

@Service
public class MyService {

    private long fixedRate = 60 * 1000; // 默认执行间隔为1分钟

    @Scheduled(fixedRate = "#{myService.fixedRate}")
    public void doTask() {
        // 执行任务的代码
    }

    public void updateFixedRate(long fixedRate) {
        this.fixedRate = fixedRate;
    }

}

这个例子中,我们定义了一个名为doTask的方法,并且使用@Scheduled注解指定了它的执行频率为fixedRate。同时,我们还定义了一个名为updateFixedRate的方法,用来动态调整定时任务的执行频率。

在updateFixedRate方法中,我们可以更新fixedRate属性的值,从而改变定时任务的执行频率。注意,我们在@Scheduled注解的fixedRate属性中使用了SpEL表达式来获取fixedRate属性的值,这样就能实现动态调整定时任务的执行频率了。

六、业务场景

6.1 从数据库中获取cron表达式,启动定时任务

可以通过在Spring Boot项目中定义一个 ScheduledTaskRegistrar bean,并在其中注册一个 Runnable 对象,以实现在项目启动后手动创建 Spring Schedule 定时任务

同时,使用 JdbcTemplate 从 MySQL 数据库中获取 cron 表达式并将其传递给 CronTrigger 对象以定义定时任务的调度时间

示例demo

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                new Runnable() {
                    @Override
                    public void run() {
                        // 这里是您的定时任务执行的逻辑
                    }
                },
                new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
                        String cronExpression = getCronExpressionFromDatabase();
                        if (cronExpression == null || cronExpression.isEmpty()) {
                            return null; // 如果 cron 表达式为空,禁止执行定时任务
                        }
                        CronTrigger cronTrigger = new CronTrigger(cronExpression);
                        return cronTrigger.nextExecutionTime(triggerContext);
                    }
                }
        );
    }

    private String getCronExpressionFromDatabase() {
        // 从 MySQL 数据库中获取 cron 表达式
        // 使用 JdbcTemplate 执行 SQL 查询,返回 cron 表达式字符串
        // 示例代码:
        String sql = "SELECT cron_expression FROM my_table WHERE task_name = 'my_task'";
        String cronExpression = jdbcTemplate.queryForObject(sql, String.class);
        return cronExpression;
    }

}

在上面的代码中,ScheduledTaskRegistrar bean 会自动在项目启动后被创建,并通过 configureTasks 方法注册了一个定时任务。该任务使用 Runnable 对象定义了任务执行的逻辑,使用 Trigger 对象定义了任务的调度时间。

Trigger 对象中,我们通过调用 getCronExpressionFromDatabase 方法从 MySQL 数据库中获取 cron 表达式,并使用 CronTrigger 对象来定义该定时任务的调度时间。在 nextExecutionTime 方法中,TriggerContext 参数可以提供有关上一次执行时间和上下文信息的详细信息。

请注意,ScheduledTaskRegistrar bean 的 @EnableScheduling 注解用于启用 Spring Schedule 功能。

6.2 从数据库中获取cron表达式,启动定时任务,任务执行时间超过下次执行时间,继续执行

在6.1的基础上继续修改

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addFixedDelayTask(
                new Runnable() {
                    @Override
                    public void run() {
                        // 这里是您的定时任务执行的逻辑
                    }
                },
                new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
                        String cronExpression = getCronExpressionFromDatabase();
                        CronTrigger cronTrigger = new CronTrigger(cronExpression);
                        Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
                        if (nextExecutionTime != null) {
                            // 如果下一次执行时间不为空,计算执行时间与当前时间之间的差值
                            long delay = nextExecutionTime.getTime() - System.currentTimeMillis();
                            if (delay < 0) {
                                // 如果下一次执行时间已经过去,设置延迟时间为 0
                                delay = 0;
                            }
                            return new Date(System.currentTimeMillis() + delay);
                        } else {
                            // 如果下一次执行时间为空,返回 null
                            return null;
                        }
                    }
                }
        );
    }

    private String getCronExpressionFromDatabase() {
        // 从 MySQL 数据库中获取 cron 表达式
        // 使用 JdbcTemplate 执行 SQL 查询,返回 cron 表达式字符串
        // 示例代码:
        String sql = "SELECT cron_expression FROM my_table WHERE task_name = 'my_task'";
        String cronExpression = jdbcTemplate.queryForObject(sql, String.class);
        return cronExpression;
    }

}

6.3 从数据库中获取定时任务,按照任务配置cron表达式启动,并监听数据库是否有新的定时任务生成

在6.2的基础上修改,原有的定时任务肯定还是不能动,那么就增加一个默认的定时任务来帮忙

  • 获取定时任务

  • 增加默认轮训定时任务

    • 去重,追加新的定时任务
  • 增加定时任务

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private JdbcTemplate jdbcTemplate;
	
	/**
     * 当前正在执行的定时任务(数据库可能突然插入)
     */
    private List<Task> registerTaskList;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		// 待执行定时任务
		List<Task> tasks = findTasksToExecute();
		if (CollUtil.isEmpty(registerTaskList)) {
            registerTaskList = new ArrayList<>();
        }
        // 追加定时任务列表
        synchronized (registerTaskList) {
            registerTaskList.addAll(tasks);
        }
		// 轮询追加定时任务
		taskRegistrar.addFixedDelayTask(
                new Runnable() {
                    @Override
                    public void run() {
						// 追加新的定时任务(数据库插入新的定时任务,自动追加执行)
						addNewScheduleTask(taskRegistrar);
                    }
                },
                new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
						// 设置当前时间和时区
						Calendar nextExecutionTime = Calendar.getInstance();
						TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); // 设置为您所在的时区
						nextExecutionTime.setTimeZone(timeZone);
						Date lastCompletionTime = triggerContext.lastCompletionTime();
						if (lastCompletionTime != null) {
							nextExecutionTime.setTime(lastCompletionTime);
						}
						// 延迟1分钟启动
						nextExecutionTime.add(Calendar.MINUTE, 1);
						// 设置定时任务的触发规则(延迟1分钟启动,每5s执行一次)
						CronTrigger cronTrigger = new CronTrigger("5 * * * * ?", timeZone);
						return cronTrigger.nextExecutionTime(triggerContext);
                    }
                }
        );
        if (!CollectionUtil.isEmpty(tasks)) {
			for(Task task : tasks) {
				String cronExpression = task.getCronExpression();
				taskRegistrar.addFixedDelayTask(
						new Runnable() {
							@Override
							public void run() {
								// 业务执行逻辑
								// executeScheduleTask(task);
							}
						},
						new Trigger() {
							@Override
							public Date nextExecutionTime(TriggerContext triggerContext) {
								CronTrigger cronTrigger = new CronTrigger(cronExpression);
								Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
								if (nextExecutionTime != null) {
									// 如果下一次执行时间不为空,计算执行时间与当前时间之间的差值
									long delay = nextExecutionTime.getTime() - System.currentTimeMillis();
									if (delay < 0) {
										// 如果下一次执行时间已经过去,设置延迟时间为 0
										delay = 0;
									}
									return new Date(System.currentTimeMillis() + delay);
								} else {
									// 如果下一次执行时间为空,返回 null
									return null;
								}
							}
						}
				);
			}

        }
    }

	/**
     * 追加新的定时任务.
     *
     * @param taskRegistrar 定时任务注册器
     */
    private void addNewScheduleTask(ScheduledTaskRegistrar taskRegistrar) {
        // 数据库中新的定时任务
        List<Task> newTaskList = findNewTasksToExecute();
        if (CollUtil.isNotEmpty(newTaskList)) {
            // 存在新的定时任务
            synchronized (registerTaskList) {
                registerTaskList.addAll(newTaskList);
            }
            for (Task newTask : newTaskList) {
                String newCronExpression = newTask.getCronExpression();
                taskRegistrar.addTriggerTask(
                        () -> {
                            // 定时任务的逻辑
                            executeScheduleTask(newTask);
                        },
                        triggerContext -> {
                            if (newCronExpression == null || newCronExpression.isEmpty()) {
                                // 如果 cron 表达式为空,禁止执行定时任务
                                return null;
                            }
                            CronTrigger cronTrigger = new CronTrigger(newCronExpression);
                            Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
                            if (nextExecutionTime != null) {
                                // 如果下一次执行时间不为空,计算执行时间与当前时间之间的差值
                                long delay = nextExecutionTime.getTime() - System.currentTimeMillis();
                                if (delay < 0) {
                                    // 如果下一次执行时间已经过去,设置延迟时间为 0
                                    delay = 0;
                                }
                                return new Date(System.currentTimeMillis() + delay);
                            } else {
                                // 如果下一次执行时间为空,返回 null
                                return null;
                            }
                        });
                // 重新加载任务
                taskRegistrar.afterPropertiesSet();
            }
        }
    }
	
	/**
     * 执行定时任务.
	 *
     * @param task 定时任务
     */
	void executeScheduleTask(Task task) {
		// ....
	}
	
	/**
     * 获取定时任务.
     *
     * @return 定时任务
     */
    private List<Task> findTasksToExecute() {
		String sql = "select * from task";
		List<Task> queryForList = jdbcTemplate.query(sql, new TaskRowMapper());
		return queryForList;
    }
	
	/**
     * 获取数据库中新的定时任务.
     *
     * @return 定时任务集合
     */
    private List<Task> findNewTasksToExecute() {
        List<Task> tasks = findTasksToExecute();
        if (CollUtil.isEmpty(tasks)) {
            return new ArrayList<>();
        }
        // 通过主键进行区分
        Map<Integer, Task> registerTaskMap = registerTaskList.stream().collect(Collectors.toMap(Task::getId, Function.identity()));
        tasks = tasks.stream().filter(e -> !registerTaskMap.containsKey(e.getKeyId())).collect(Collectors.toList());
        return tasks;
    }

}

6.4 启动定时任务,任务结束时,关闭定时任务

关闭定时任务

  • 通过ScheduledTaskRegistrar.destroy()方法

    • 关闭ScheduledTaskRegistrar中注册的所有定时任务(一键关闭)
  • 通过ScheduledFuture.cancel(true)方法关闭当前定时任务

    • 关闭ScheduledFuture注册的定时任务(选择关闭)
基于ScheduledTaskRegistrar.destroy()

基于代码6.1进行改造

@@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		Task task = new Task();
        taskRegistrar.addTriggerTask(
                new Runnable() {
                    @Override
                    public void run() {
                        // 这里是您的定时任务执行的逻辑
						excute(task);
						if (isClose(task)) {
							// 符合关闭条件,关闭定时任务
							taskRegistrar.destroy();
						}
                    }
                },
                new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
                        String cronExpression = getCronExpressionFromDatabase();
                        if (cronExpression == null || cronExpression.isEmpty()) {
                            return null; // 如果 cron 表达式为空,禁止执行定时任务
                        }
                        CronTrigger cronTrigger = new CronTrigger(cronExpression);
                        return cronTrigger.nextExecutionTime(triggerContext);
                    }
                }
        );
    }

    private String getCronExpressionFromDatabase() {
        // 从 MySQL 数据库中获取 cron 表达式
        // 使用 JdbcTemplate 执行 SQL 查询,返回 cron 表达式字符串
        // 示例代码:
        String sql = "SELECT cron_expression FROM my_table WHERE task_name = 'my_task'";
        String cronExpression = jdbcTemplate.queryForObject(sql, String.class);
        return cronExpression;
    }

}
基于ScheduledFuture.cancel(true)

基于代码6.1改造

定义TaskScheduler

@Configuration
@EnableScheduling
public class TaskSchedulerConfig {

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 设置线程池大小
        taskScheduler.setPoolSize(15);
        // 设置线程名前缀
        taskScheduler.setThreadNamePrefix("MyTaskScheduler-");
        // 设置等待线程池关闭的时间
        taskScheduler.setAwaitTerminationSeconds(3600);
        // 在关闭时等待所有任务完成
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        // 其他定制配置...
        return taskScheduler;
    }
}

定义定时任务

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private JdbcTemplate jdbcTemplate;
	
	/**
     * 定时任务(用于关闭某项定时任务)
     */
    @Autowired
    private TaskScheduler taskScheduler;
	/**
     * 定时任务(用于关闭某项定时任务)
     */
	private ScheduledFuture<?> scheduledFuture;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		taskRegistrar.setTaskScheduler(taskScheduler);
		Task task = new Task();
        scheduledFuture = taskScheduler.schedule(
                new Runnable() {
                    @Override
                    public void run() {
                        // 这里是您的定时任务执行的逻辑
						excute(task);
						if (isClose(task)) {
							// 符合关闭条件,关闭定时任务
							scheduledFuture.close(true);
						}
                    }
                },
                new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
                        String cronExpression = getCronExpressionFromDatabase();
                        if (cronExpression == null || cronExpression.isEmpty()) {
                            return null; // 如果 cron 表达式为空,禁止执行定时任务
                        }
                        CronTrigger cronTrigger = new CronTrigger(cronExpression);
                        return cronTrigger.nextExecutionTime(triggerContext);
                    }
                }
        );
    }

    private String getCronExpressionFromDatabase() {
        // 从 MySQL 数据库中获取 cron 表达式
        // 使用 JdbcTemplate 执行 SQL 查询,返回 cron 表达式字符串
        // 示例代码:
        String sql = "SELECT cron_expression FROM my_table WHERE task_name = 'my_task'";
        String cronExpression = jdbcTemplate.queryForObject(sql, String.class);
        return cronExpression;
    }

}

通过taskScheduler.schedule创建任务,依据判断条件isClose()关闭定时任务

6.5 从数据库中获取定时任务,按照任务配置cron表达式启动,并监听数据库是否有新的定时任务生成,任务执行完毕后,关闭当前定时任务.

基于6.3,6.4代码进行改造!!!

由于我们采用的是一次定义多任务,但关闭时要求只关闭当前任务,所以不能采用

ScheduledTaskRegistrar.destroy(),它会将所有任务一起关闭,源码如下:

	@Override
	public void destroy() {
	    for (ScheduledTask task : this.scheduledTasks) {
			task.cancel();
		}
		if (this.localExecutor != null) {
			this.localExecutor.shutdownNow();
		}
	}

ScheduledTask.cancel 源码如下:

	public void cancel() {
		ScheduledFuture<?> future = this.future;
		if (future != null) {
			future.cancel(true);
		}
	}

与我们的ScheduledFutur.cancel一致,定点删除,因此我们开始正题

定义TaskScheduler

@Configuration
@EnableScheduling
public class TaskSchedulerConfig {

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 设置线程池大小
        taskScheduler.setPoolSize(15);
        // 设置线程名前缀
        taskScheduler.setThreadNamePrefix("MyTaskScheduler-");
        // 设置等待线程池关闭的时间
        taskScheduler.setAwaitTerminationSeconds(3600);
        // 在关闭时等待所有任务完成
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        // 其他定制配置...
        return taskScheduler;
    }
}

定义定时任务

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Autowired
    private JdbcTemplate jdbcTemplate;
	
	/**
     * 当前正在执行的定时任务(数据库可能突然插入)
     */
    private List<Task> registerTaskList;
	
	/**
     * 定时任务(用于关闭某项定时任务)
     */
    @Autowired
    private TaskScheduler taskScheduler;

    /**
     * 存储所有的定时任务map<唯一标识,定时任务>
     */
    private Map<String, ScheduledFuture> scheduledFutureMap;


    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		// 待执行定时任务
		List<Task> tasks = findTasksToExecute();
		if (CollUtil.isEmpty(registerTaskList)) {
            registerTaskList = new ArrayList<>();
        }
        // 追加定时任务列表
        synchronized (registerTaskList) {
            registerTaskList.addAll(tasks);
        }
		
		taskRegistrar.setTaskScheduler(taskScheduler);
		
		// 轮询追加定时任务
		ScheduledFuture<?> defaultScheduledFuture = taskScheduler.schedule(
                new Runnable() {
                    @Override
                    public void run() {
						// 追加新的定时任务(数据库插入新的定时任务,自动追加执行)
						addNewScheduleTask(taskRegistrar);
                    }
                },
                new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
						// 设置当前时间和时区
						Calendar nextExecutionTime = Calendar.getInstance();
						TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); // 设置为您所在的时区
						nextExecutionTime.setTimeZone(timeZone);
						Date lastCompletionTime = triggerContext.lastCompletionTime();
						if (lastCompletionTime != null) {
							nextExecutionTime.setTime(lastCompletionTime);
						}
						// 延迟1分钟启动
						nextExecutionTime.add(Calendar.MINUTE, 1);
						// 设置定时任务的触发规则(延迟1分钟启动,每5s执行一次)
						CronTrigger cronTrigger = new CronTrigger("5 * * * * ?", timeZone);
						return cronTrigger.nextExecutionTime(triggerContext);
                    }
                }
        );
		// 管理默认任务
		scheduledFutureMap.put("default", defaultScheduledFuture);
        if (!CollectionUtil.isEmpty(tasks)) {
			for(Task task : tasks) {
				String cronExpression = task.getCronExpression();
				ScheduledFuture<?> schedule = taskScheduler.schedule(
						new Runnable() {
							@Override
							public void run() {
								// 业务执行逻辑
								// executeScheduleTask(task);
							}
						},
						new Trigger() {
							@Override
							public Date nextExecutionTime(TriggerContext triggerContext) {
								CronTrigger cronTrigger = new CronTrigger(cronExpression);
								Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
								if (nextExecutionTime != null) {
									// 如果下一次执行时间不为空,计算执行时间与当前时间之间的差值
									long delay = nextExecutionTime.getTime() - System.currentTimeMillis();
									if (delay < 0) {
										// 如果下一次执行时间已经过去,设置延迟时间为 0
										delay = 0;
									}
									return new Date(System.currentTimeMillis() + delay);
								} else {
									// 如果下一次执行时间为空,返回 null
									return null;
								}
							}
						}
				);
				// 通过主键作为唯一标识,退出时获取scheduledFuture退出
				String identity = String.valueOf(task.getId());
				scheduledFutureMap.put(identity, schedule);
			}
        }
    }

	/**
     * 追加新的定时任务.
     *
     * @param taskRegistrar 定时任务注册器
     */
    private void addNewScheduleTask(ScheduledTaskRegistrar taskRegistrar) {
        // 数据库中新的定时任务
        List<Task> newTaskList = findNewTasksToExecute();
        if (CollUtil.isNotEmpty(newTaskList)) {
            // 存在新的定时任务
            synchronized (registerTaskList) {
                registerTaskList.addAll(newTaskList);
            }
            for (Task newTask : newTaskList) {
                String newCronExpression = newTask.getCronExpression();
                ScheduledFuture<?> schedule = taskScheduler.schedule(
                        () -> {
                            // 定时任务的逻辑
                            executeScheduleTask(newTask);
                        },
                        triggerContext -> {
                            if (newCronExpression == null || newCronExpression.isEmpty()) {
                                // 如果 cron 表达式为空,禁止执行定时任务
                                return null;
                            }
                            CronTrigger cronTrigger = new CronTrigger(newCronExpression);
                            Date nextExecutionTime = cronTrigger.nextExecutionTime(triggerContext);
                            if (nextExecutionTime != null) {
                                // 如果下一次执行时间不为空,计算执行时间与当前时间之间的差值
                                long delay = nextExecutionTime.getTime() - System.currentTimeMillis();
                                if (delay < 0) {
                                    // 如果下一次执行时间已经过去,设置延迟时间为 0
                                    delay = 0;
                                }
                                return new Date(System.currentTimeMillis() + delay);
                            } else {
                                // 如果下一次执行时间为空,返回 null
                                return null;
                            }
                        });
				// 通过主键作为唯一标识,退出时获取scheduledFuture退出
				String identity = String.valueOf(task.getId());
				scheduledFutureMap.put(identity, schedule);
                // 重新加载任务
                taskRegistrar.afterPropertiesSet();

            }
        }
    }
	
	/**
     * 执行定时任务.
	 *
     * @param task 定时任务
     */
	void executeScheduleTask(Task task) {
		// 定时任务
		
		// 判断是否关闭当前定时任务
		if (isClose(task)) {
			String identity = String.valueOf(task.getId());
			if (scheduledFutureMap.containsKey(identity)) {
				ScheduledFuture scheduledFuture = scheduledFutureMap.get(identity);
				// 取消当前任务
				scheduledFuture.cancel(true);
				scheduledFutureMap.remove(identity);
				return;
			}
		}
	}
	
	/**
     * 获取定时任务.
     *
     * @return 定时任务
     */
    private List<Task> findTasksToExecute() {
		String sql = "select * from task";
		List<Task> queryForList = jdbcTemplate.query(sql, new TaskRowMapper());
		return queryForList;
    }
	
	/**
     * 获取数据库中新的定时任务.
     *
     * @return 定时任务集合
     */
    private List<Task> findNewTasksToExecute() {
        List<Task> tasks = findTasksToExecute();
        if (CollUtil.isEmpty(tasks)) {
            return new ArrayList<>();
        }
        // 通过主键进行区分
        Map<Integer, Task> registerTaskMap = registerTaskList.stream().collect(Collectors.toMap(Task::getId, Function.identity()));
        tasks = tasks.stream().filter(e -> !registerTaskMap.containsKey(e.getKeyId())).collect(Collectors.toList());
        return tasks;
    }

}

七、总结

通过本文的介绍,我们可以看到,在Spring Boot应用中整合Spring Schedule非常简单,只

需要添加依赖、配置定时任务,就能实现定时任务的执行。同时,我们还介绍了如何解决任

务并发执行的问题,以及如何动态调整定时任务的执行时间,动态关闭定时任务。

希望这篇文章对您有所帮助。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值