业务场景:需要开启一个定时任务每天执行,任务执行时机通过配置文件获取,但是配置参数不能用cron表达式,要用更容易理解的时间点(时分秒)
首先spring boot开启定时任务,使用@EnableScheduling注解,这个注解写到启动类上或者其他的spring管理的bean上都行
接下来就是编写定时任务了,常用有两种方式,一个是基于注解@Scheduled,另一个是实现接口SchedulingConfigurer
1.基于注解方式
执行定时任务的类一定要被spring管理,可以加@Component注解,然后在方法上加@Scheduled注解就可以了。
package ***.client.task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class test {
private static final Logger logger = LoggerFactory.getLogger(SpringDynamicCornTask.class);
@Scheduled(cron = "0/5 * * * * ?")
private void test() {
logger.info("业务处理逻辑。。。5秒一次");
}
}
执行结果:
@Scheduled注解有以下几个属性,使用时至少要指定cron,fixedDelay,fixedRat3个属性中的一个。且属性的值支持${}占位符从配置文件获取
例如配置文件加参数:
占位符获取参数
这种基于注解的方式很快捷,但是不符合我的需求,虽然可以从配置文件读取参数,但是无法将配置文件中的参数处理翻译为cron表达式。
2.基于接口的方式
创建接口实现类
package ***.client.task;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import ***.client.execute.ExecuteTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
/**
* 下载报告定时任务
*/
@Component
public class SpringDynamicCornTask implements SchedulingConfigurer {
private static final Logger logger = LoggerFactory.getLogger(SpringDynamicCornTask.class);
@Resource
private ExecuteTask executeTask;
private static final String DEFAULT_CRON = "00 00 22 * * ?";
private static String cronMode = "%s %s %s * * ?";
@Value("${task.cron.time}")
private String default_cron;
/**
* 定时任务配置
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(() -> {
logger.info("定时任务执行");
executeTask.run();
}, triggerContext -> {
logger.info("配置定时任务下一次触发时间");
CronTrigger trigger = new CronTrigger(initCron());
return trigger.nextExecutionTime(triggerContext);
});
}
/**
* 初始化定时任务cron
*/
private String initCron() {
if (!StringUtils.isEmpty(default_cron)) {
String[] cronArray;
if (default_cron.contains(":")) {
cronArray = default_cron.split(":");
} else if (default_cron.contains(":")) {
cronArray = default_cron.split(":");
} else if (NumberUtil.isInteger(default_cron)) {
String time = DateUtil.secondToTime(Integer.valueOf(default_cron));
cronArray = time.split(":");
} else {
logger.error("定时任务参数读取失败,使用默认参数");
return DEFAULT_CRON;
}
String hour = (cronArray.length > 0 && NumberUtil.isInteger(cronArray[0]) && NumberUtil.parseInt(cronArray[0]) <= 24) ? cronArray[0] : "22";
String minute = (cronArray.length > 1 && NumberUtil.isInteger(cronArray[0]) && NumberUtil.parseInt(cronArray[0]) <= 60) ? cronArray[1] : "00";
String second = (cronArray.length > 2 && NumberUtil.isInteger(cronArray[0]) && NumberUtil.parseInt(cronArray[0]) <= 60) ? cronArray[2] : "00";
String cron = String.format(cronMode, second, minute, hour);
logger.info("定时任务cron表达式:{}", cron);
return cron;
} else {
logger.error("定时任务参数读取失败,使用默认参数");
return DEFAULT_CRON;
}
}
}
配置文件中的参数:
我们在实现类中通过自定义的方法来将时分秒翻译为cron表达式,来实现自己的需求。
在实现类中,每次定时任务业务执行完后都会配置触发器下次执行时间,如果我们想要动态控制定时任务的触发时机,在获取下次执行时间的地方改造自己的实现方法,读取数据库或者redis都可以
执行结果: