最近做的云平台项目中定时任务的模块使用到了quartz,定时表达式需从数据库中读取并执行。
特意学习并记录一下。
表达式说明:
corn从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份
秒(Seconds) | 0~59的整数 | , - * / | 四个字符 | 分(Minutes) | 0~59的整数 | , - * / | 四个字符 | 小时(Hours) | 0~23的整数 | , - * / | 四个字符 | 日期(DayofMonth) | 1~31的整数(但是需要考虑你月的天数) | ,- * ? / L W C | 八个字符 | 月份(Month) | 1~12的整数或者 JAN-DEC | , - * / | 四个字符 | 星期(DayofWeek) | 1~7的整数或者 SUN-SAT (1=SUN) | , - * ? / L C # | 八个字符 | 年(可选,留空)(Year) | 1970~2099 | , - * / | 四个字符 |
1)*:表示匹配该域的任意值。假如在Minutes域使用*, 即表示每分钟都会触发事件。
2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.
5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。
举例:
* * * * * ? * -> 任务开始后,每秒都执行
1-5 * * * * ? * -> 任务开始后,每分钟的1-5秒执行
4/4 * * * * ? * -> 任务开始后过4秒开始执行,再每隔4秒执行
3,5,7,9 * * * * ? * -> 任务开始后下一分钟,指定秒数执行
10 * * * * ? * -> 任务开始的每个整数分钟第10秒执行
0 1/7 * * * ? -> 任务开始后过一分钟,每隔7分钟(即7的倍数的分钟数)的第0秒执行
5 1-3 * * * ? -> 任务开始的下一个指定分钟数内每分钟的第5秒执行
10 3,4,6 * * * ? * -> 任务开始指定分钟数的第10秒执行
3 0,3 * * * ? * -> 任务开始,每小时的第指定分钟指定秒钟执行
* 0,3 * * * ? * -> 任务开始,每小时的第指定分钟每一秒钟执行
0 7,9 3-4 * * ? * -> 任务开始后,指定小时数内指定分钟的第指定秒数执行
* 7,9 3-4 * * ? * -> 任务开始后,指定小时数内指定分钟的每一秒数执行
0 * 3-4 * * ? * -> 任务开始后,指定小时数内每一分钟的第指定秒数执行
0 0 2 * * ? * -> 任务开始后,每次指定小时数的第指定分钟数第指定秒钟数执行
* * 2 * * ? * -> 任务开始后,每次指定小时数的每一分钟数每一秒钟数执行
0 0 0 * * ? * -> 任务开始,每一日的指定小时指定分钟指定秒钟数执行
0 0 0 2-3 * ? * -> 任务开始后,每次指定日期数的指定小时指定分钟指定秒钟数执行
0 0 0 2/4 * ? * -> 任务开始,过指定天数(此处是2)时执行,往后每隔指定天数(此处是4)执行
0 0 0 L * ? * -> 任务开始后,每月最后一条执行
0 0 0 3,4,5,6 * ? * -> 任务开始后,每月中的指定日期数执行
0 0 2 1 * ? * 表示在每月的1日的凌晨2点调整任务
0 15 10 ? * MON-FRI 2019 表示2019年每月的周一到周五每天上午10:15执行作业
0 15 10 ? * MON 2019 表示2019年每月的周一每天上午10:15执行作业
0 0 12 ? * WED * 表示每个星期三中午12点
0 15 10 ? * MON-FRI * 表示每年每月周一至周五的上午10:15触发
0 15 10 L 3 ? * 表示每年3月最后一天上午10:15触发
0 15 10 ? * 6L * 每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 * 每月的第三个星期五上午10:15触发
时间点:
每年的这个时间点都会执行

指定年份(年分段/点)的时间点执行


注意:Spring Task是Quartz的弱版,quartz支持年份,而Spring Task不支持
代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
CREATE TABLE `schedule_task` (
`id` int(50) NOT NULL AUTO_INCREMENT COMMENT '数据自增Id',
`cron` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
import com.schedule.my.schedule.schedule.utils.TransformationUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Date;
@Configuration
@EnableScheduling
@Component
public class QuartzScheduleTask implements SchedulingConfigurer {
@Mapper
public interface CronMapper {
@Select("select cron from schedule_task limit 1")
String getCron();
}
@Autowired
@SuppressWarnings("all")
CronMapper cronMapper;
/**
* 执行定时任务。读取数据库中存储的cron表达式执行定时任务
* @param scheduledTaskRegistrar
* 添加的是TriggerTask,目的循环读取数据库中设置好的执行周期
*
* 这个解析cron的地方只支持六位!“Quartz”根本就不是“Quartz” (项目名字就是 Company_Quartz),实际是Spring Task。
* Spring Task是Quartz的弱版,quartz支持年份,而Spring Task不支持。
*/
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
Date date = new Date();
Date time = new Date("2019/1/23 11:26:05");
System.out.println("进入Quartz定时任务" + date);
//1.添加任务内容(Runnable)
scheduledTaskRegistrar.addTriggerTask(new Runnable() {
@Override
public void run() {
System.out.println("Quartz执行定时任务: " + date);
}
//设置执行周期(Trigger)
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
/**
* 注:此处实为Spring Task,不支持年份,所以cron只有6个域
*/
//从数据库获取执行周期
//String cron = cronMapper.getCron();
String cron = "06 50 09 06 07 ?";
//String cron = TransformationUtils.formatDateByPattern(time);
System.out.println("====Cron:" + cron);
//合法性校验.
if (StringUtils.isEmpty(cron)) {
System.out.println("===Cron不可为空!");
}
int oCronLength = cron.length();
cron.replaceAll(" ", "");
if ((oCronLength - cron.length()) > 5) {
System.out.println("===Cron不支持(超过6位)!");
}
//返回执行周期(Date)
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
});
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 转换工具
*/
public class TransformationUtils {
/**
* cron表达式支持7域,即支持年份
*/
static final String Quartz_date_fromat = "ss mm HH dd MM ? yyyy";
/**
* cron表达式支持6域,即不支持年份
*/
static final String TASK_DATE_FORMAT = "ss mm HH dd MM ?";
/**
* 日期转换成cron表达式
* @param date
* @return
*/
public static String formatDateByPattern(Date date) {
SimpleDateFormat format = new SimpleDateFormat(TASK_DATE_FORMAT);
String formatTimeStr = null;
if (date != null) {
formatTimeStr = format.format(date);
}
return formatTimeStr;
}
/*public static void main(String[] args) {
// System.out.println(formatDateByPattern(new Date(), DATE_FORMAT));
// System.out.println(new Date("2018/7/6/ 9:47:06"));
}*/
}
碰到一个cron的错误:
Support for specifying both a day-of-week AND a day-of-month parameter is not implemented
Cron表达式,原因是:天(月的多少号)和天(周几)不能同时都为*,当有一个为*时,另一个要是?,才行。
|