2.1.12 定时任务
Spring 框架自带任务调度功能,好比一个轻量级的Quartz,使用简单、方便,不需要依赖其他JAR包。
只需要在项目主程序启动类上添加@EnableScheduling开启任务调度功能即可
@SpringBootApplication
@EnableScheduling
public class LearnApplication {
public static void main(String[] args) {
SpringApplication.run(LearnApplication.class, args);
}
}
2.1.12.1 简单定时任务
@Component
public class TestTask {
@Scheduled(cron = "0/10 * * * * *")
public void testTask1() {
System.out.println("【任务一】测试定时任务" + LocalDateTime.now());
}
}
如上述,配置一个简单的定时任务只需要在调度方法上添加@Shceduled注解即可,就可以使用定时任务。
2.1.12.2 异步定时任务
@Component
// 开启异步支持
@EnableAsync
public class TestTask {
@Scheduled(cron = "0/10 * * * * *")
// 方法使用异步执行,每次任务创建一个线程执行任务
@Async
public void testTask1() {
System.out.println("【任务一】测试定时任务" + LocalDateTime.now() + " " + Thread.currentThread().getName());
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【任务一】休眠" + (i + 1) + "秒测试定时任务" + LocalDateTime.now() + " " + Thread.currentThread().getName());
}
}
}
往往在我们的项目调度任务中,有的场景是需要在当前任务还没有执行完毕时,就需要执行下一个定时调度任务,在这种情况下需要使用异步的方式来执行定时任务。
@EnableAsync开启异步支持
@Async标记任务使用异步执行(下次任务将在下一个配置时间开始,不等待当前任务执行完毕)
2.1.12.3 动态定时任务
当我们编写定时任务是,流程大致为:编码->配置执行周期->启动服务。
当前我们配置的执行周期是每天早上8点执行,当我们有天,需求变更,需要每天晚上8点执行,我们的操作流程为:修改执行周期->新版打包->停服->启动新版服务。整个流程线步骤多,存在不可控因素。
那么我们怎么做到不停服更新我们的执行周期呢??
那么下面我们模拟将cron表达式存储在MySQL。
1)定义cron相关service
// 表达式相关接口
public interface SwitchService {
/**
* 获取最新 cron 表达式
*
* @param taskId 任务ID
* @return 最新 cron表达式
*/
String getCron(String taskId);
/**
* 修改 cron 表达式
*/
void modify();
}
// 表达式相关接口实现
@Service
public class SwitchServiceImpl implements SwitchService {
private static String DB_CRON = "";
@Override
public String getCron(String taskId) {
System.out.println("执行数据库查询 DB_CRON " + LocalDateTime.now());
return DB_CRON;
}
@Override
public void modify() {
DB_CRON = "0/20 * * * * *";
System.out.println("修改数据库中 DB_CRON " + LocalDateTime.now());
}
}
此处模拟修改以及查询
2)创建具体任务执行
@Component
public class DynamicCronTask implements SchedulingConfigurer {
// 模拟当前任务ID
private String TASK_ID = "5001";
@Autowired
private SwitchService switchService;
private String SpringDynamicCronTask() {
// 默认为 每5秒执行
String cron = "0/5 * * * * ?";
//从数据库获得配置的corn表达式
String dbCron = switchService.getCron(TASK_ID);
// 当查询为空时,使用默认的表达式
if (StringUtils.isNotBlank(dbCron)) {
return dbCron;
}
return cron;
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
// 任务逻辑
System.out.println("执行任务逻辑...." + LocalDateTime.now());
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
String s = SpringDynamicCronTask();
// 任务触发,可修改任务的执行周期
CronTrigger trigger = new CronTrigger(s);
Date nextExec = trigger.nextExecutionTime(triggerContext);
return nextExec;
}
});
}
}
3)启动服务
查看执行日志
执行数据库查询 DB_CRON 2020-06-09T10:33:30.001
执行任务逻辑....2020-06-09T10:33:35.002
执行数据库查询 DB_CRON 2020-06-09T10:33:35.002
执行任务逻辑....2020-06-09T10:33:40.001
执行数据库查询 DB_CRON 2020-06-09T10:33:40.001
修改数据库中 DB_CRON 2020-06-09T10:33:42.085
执行任务逻辑....2020-06-09T10:33:45
执行数据库查询 DB_CRON 2020-06-09T10:33:45
执行任务逻辑....2020-06-09T10:34:00.001
执行数据库查询 DB_CRON 2020-06-09T10:34:00.001
执行任务逻辑....2020-06-09T10:34:20.002
执行数据库查询 DB_CRON 2020-06-09T10:34:20.002
通过日志可以看出,在应用启动时,会首先从数据库中查询配置的执行周期,然后执行定时任务,执行完毕后会再次查询执行周期,下一个执行时间结束后就会按照修改的执行时间执行。
生效时间为下一个执行时间结束后,做不到立即生效!!!