Springboot整合Quartz定时任务
使用
以下使用maven作为依赖
添加依赖
<!-- springboot quartz-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
spring配置文件
spring:
#定时任务配置
quartz:
job-store-type: jdbc
properties:
org:
quartz:``````````````
scheduler:
instanceName: quartzScheduler
instanceId: AUTO
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
#自定义的数据源别名,与最下方一致
dataSource: qrtzDS
#与cron的调度有关,忽略重启期间的任务
misfireThreshold: 10000
#设置为true开启集群
isClustered: false
clusterCheckinInterval: 10000
#如果开启,会没法给任务传参数
useProperties: false
dataSource:
qrtzDS:
driver: com.mysql.cj.jdbc.Driver
URL: jdbc:mysql://127.0.0.1:3306/qrtz?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
user: user
password: 123456
maxConnections: 10
provider: hikaricp
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 20
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
代码实现
sql
create table sas_quartz_job
(
job_id bigint default 0 not null comment '任务id'
primary key,
job_name varchar(200) null comment '任务名称',
bean_name varchar(200) charset utf8 null comment '类',
method_name varchar(100) charset utf8 null comment '方法',
params text charset utf8 null comment '参数',
cron_expression varchar(100) charset utf8 null comment '表达式',
status tinyint null comment '任务状态,0:暂停,1:正常, 2:已执行',
remark varchar(200) charset utf8 null comment '备注',
create_by varchar(200) charset utf8 null comment '创建人',
create_time datetime null comment '创建时间',
update_by varchar(200) charset utf8 null comment '更新人',
update_time datetime null comment '更新时间',
is_delete tinyint(1) null comment '删除标记',
source_id bigint(19) null comment '谁的主键关联的该定时任务'
);
石英日程配置
@Configuration
public class QuartzScheduleConfig {
@Resource
QuartzJobService quartzJobService;
@Resource
private SchedulerFactoryBean schedulerFactoryBean;
@SneakyThrows
@Bean
public Scheduler scheduler() {
return schedulerFactoryBean.getScheduler();
}
/**
* 项目启动,初始化任务
* 1. 从数据库中加载存在的,可执行的定时任务
* 2. 清除僵尸任务,如果当前定时任务的cron表达式不足以执行一次,那么就干掉这个任务
* 系统可能存在一次性的定时任务,该种任务已创建,但是由于部分情况(如:定时服务重启或关闭,导致定时任务未执行的情况)导致任务并不能正常执行,
* 这个时候问题来了,这些任务可能一直躺在数据库中并不会被执行,但每次都会被加载到内存中,
* 所以,这里需要清理掉这些不会被再次运行的任务
*/
@PostConstruct
public void init() throws ParseException {
List<QuartzJob> jobList = quartzJobService.list(new QuartzJob());
List<Long> preRemovedJobIds = new ArrayList<>();
for (QuartzJob job : jobList) {
/*判断当前cron表达式是否至少会执行一次,会则创建定时任务,不会则删除定时任务*/
if (new CronExpression(job.getCronExpression()).getTimeAfter(new Date()) != null) {
CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(job.getJobId());
//如果不存在,则创建
if (cronTrigger == null) {
ScheduleUtils.createScheduleJob(job);
} else {
ScheduleUtils.updateScheduleJob(job);
}
} else {
preRemovedJobIds.add(job.getJobId());
}
}
if (preRemovedJobIds.size() > 0) {
QuartzJob job = new QuartzJob();
job.setJobIds(preRemovedJobIds.toArray(new Long[preRemovedJobIds.size()]));
quartzJobService.removeByJobId(job);
}
}
}
实体类(QuartzJob,石英任务):
@Data
@TableName(value = "quartz_job")
public class QuartzJob {
/**
* 任务调度参数key
*/
public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
@TableId(value = "job_id", type = IdType.ASSIGN_ID)
@Schema(description = "任务id")
private Long jobId;
@Schema(description = "任务名称")
@TableField(value = "job_name")
private String jobName;
@Schema(description = "谁的主键关联的该定时任务")
@TableField(value = "source_id")
private Long sourceId;
@TableField(value = "bean_name")
@Schema(description = "类")
private String beanName;
@TableField(value = "method_name")
@Schema(description = "方法")
private String methodName;
@TableField(value = "params")
@Schema(description = "参数")
private String params;
@TableField(value = "cron_expression")
@Schema(description = "表达式")
private String cronExpression;
@TableField(value = "status")
@Schema(description = "任务状态,0:暂停,1:正常")
private Integer status;
@TableField(value = "remark")
@Schema(description = "备注")
private String remark;
@TableField(value = "create_by", fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
@TableField(value = "create_time", fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新人")
private String updateBy;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@TableField(value = "is_delete", fill = FieldFill.INSERT)
@Schema(description = "删除标记")
private Boolean deleted;
}
mapper(QuartzJobMapper,石英任务数据访问对象):
@Mapper
public interface QuartzJobMapper extends BasicMapper<QuartzJob> {
@MapKey("id")
IPage<Map<String, Object>> pageByQuery(IPage<?> page, @Param(Constants.WRAPPER) Wrapper<QuartzJob> wrapper);
}
service (QuartzJobService,石英任务服务对象):
public interface QuartzJobService extends IService<QuartzJob> {
/**
* 插入数据
*
* @param entity
* @return
*/
int insert(QuartzJob job);
/**
* 更新数据
*
* @param entity
* @return
*/
int update(QuartzJob job);
/**
* 根据id获取数据
*
* @param entity
* @return
*/
QuartzJob detailByJobId(QuartzJob job);
/**
* 获取分页数据
*
* @param entity
* @return
*/
IPage<QuartzJob> page(QuartzJob job);
/**
* 查询列表
*
* @param entity
* @return
*/
List<QuartzJob> list(QuartzJob job);
/**
* 删除数据
*
* @param entity
* @return
*/
boolean removeByJobId(QuartzJob job);
/**
* 分页查询任务
* @param entity 查询参数对象
* @return 分页对象
*/
@MapKey("id")
IPage<Map<String,Object>> pageByQuery(QuartzJob job);
/**
* 新增任务
* @param job 要新增的定时任务对象
* @return 是否保存成功
*/
boolean saveQuartzJob(QuartzJob job);
/**
* 根据id查询任务
* @param jobId 要查询的任务ID
* @return 定时任务对象
*/
QuartzJob getQuartzJobById(Long jobId);
/**
* 修改任务
* @param job 要更新的定时任务对象
* @return 是否更新成功
*/
boolean updateQuartzJob(QuartzJob job);
/**
* 批量删除任务
* @param id 要批量删除的定时任务ID列表
* @return 已删除数量
*/
int batchRemoveQuartzJob(Long[] id);
/**
* 立即运行任务
* @param id 要执行定时任务的ID列表
*/
void run(Long[] id);
/**
* 暂停任务
* @param id 要暂停的任务ID列表
* @return 是否暂停成功
*/
boolean pause(Long[] id);
/**
* 恢复任务
* @param id 要恢复的定时任务ID列表
* @return 是否恢复成功
*/
boolean resume(Long[] id);
}
实现类(QuartzJobService,石英任务服务实现对象):
@Service
public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob> implements QuartzJobService {
@Resource
QuartzJobMapper quartzJobMapper;
@Resource
private SnowflakeIdWorker snowflakeIdWorker;
public int insert(QuartzJob entity) {
entity.setJobId(snowflakeIdWorker.nextId());
entity.setCreateBy(LoginUtil.getLoginUser().getRealname());
entity.setCreateTime(LocalDateTime.now());
return quartzJobMapper.insert(entity);
}
@Override
public int update(QuartzJob entity) {
QueryWrapper<QuartzJob> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("job_id", entity.getJobId());
queryWrapper.eq("is_delete", 0);
return quartzJobMapper.update(entity, queryWrapper);
}
@Override
public QuartzJob detailByJobId(QuartzJob entity) {
QueryWrapper<QuartzJob> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("job_id", entity.getJobId());
queryWrapper.eq("is_delete", 0);
QuartzJob result = quartzJobMapper.selectOne(queryWrapper);
if (entity == null) {
return null;
}
return result;
}
@Override
public List<QuartzJob> list(QuartzJob entity) {
List<QuartzJob> list = quartzJobMapper.list();
return list;
}
@Override
public IPage<QuartzJob> page(QuartzJob entity) {
LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.isNotBlank(entity.getBeanName()), QuartzJob::getBeanName, entity.getBeanName())
.eq(StringUtils.isNotBlank(entity.getMethodName()), QuartzJob::getMethodName, entity.getMethodName())
.eq(entity.getStatus() != null, QuartzJob::getStatus, entity.getStatus())
.ge(StringUtils.isNotBlank(entity.getStartDate()), QuartzJob::getCreateTime, entity.getStartDate())
.le(StringUtils.isNotBlank(entity.getEndDate()), QuartzJob::getCreateTime, entity.getEndDate());
IPage<Map<String, Object>> iPage = quartzJobMapper.pageByQuery(entity.createPage(), wrapper);
return iPage;
}
@Override
public boolean removeByJobId(QuartzJob entity) {
for (Long jobId : entity.getJobIds()) {
ScheduleUtils.deleteScheduleJob(jobId);
}
return quartzJobMapper.deleteBatchIds(Arrays.asList(entity.getJobIds())) > 0;
}
/**
* 分页查询任务
*
* @param entity 定时任务查询对象
* @return 分页Map对象
*/
@Override
public IPage<Map<String, Object>> pageByQuery(QuartzJob entity) {
LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(entity.getStatus() != null, QuartzJob::getJobId, entity.getJobId())
.eq(entity.getJobFlag() != null, QuartzJob::getJobFlag, entity.getJobFlag())
.eq(StringUtils.isNotBlank(entity.getBeanName()), QuartzJob::getBeanName, entity.getBeanName())
.eq(StringUtils.isNotBlank(entity.getMethodName()), QuartzJob::getMethodName, entity.getMethodName())
.eq(entity.getStatus() != null, QuartzJob::getStatus, entity.getStatus())
.ge(StringUtils.isNotBlank(entity.getStartDate()), QuartzJob::getCreateTime, entity.getStartDate())
.le(StringUtils.isNotBlank(entity.getEndDate()), QuartzJob::getCreateTime, entity.getEndDate());
return quartzJobMapper.pageByQuery(entity.createPage(), wrapper);
}
/**
* 新增任务
*
* @param job 任务对象
* @return 是否成功
*/
@Override
public boolean saveQuartzJob(QuartzJob job) {
job.setJobId(job.getJobId() == null ? snowflakeIdWorker.nextId() : job.getJobId());
job.setStatus(QuartzJobStatusEnum.NORMAL.getValue());
QuartzJob QuartzJob = BeanCopyUtil.copyProperties(job, QuartzJob::new);
QuartzJob.setCreateBy(LoginUtil.getLoginUser().getRealname());
QuartzJob.setCreateTime(LocalDateTime.now());
QuartzJob.setDeleted(false);
int count = quartzJobMapper.insert(QuartzJob);
ScheduleUtils.createScheduleJob(job);
return count > 0;
}
/**
* 根据id查询任务
*
* @param jobId 任务ID
* @return 任务对象
*/
@Override
public QuartzJob getQuartzJobById(Long jobId) {
QuartzJob job = quartzJobMapper.selectById(jobId);
return BeanCopyUtil.copyProperties(job, QuartzJob::new);
}
/**
* 更新任务
*
* @param entity 任务对象
* @return 是否成功
*/
@Override
public boolean updateQuartzJob(Sasentity entity) {
QuartzJob jobDO = new QuartzJob();
jobDO.setJobId(entity.getJobId());
jobDO.setSourceId(entity.getSourceId());
jobDO.setStatus(entity.getStatus());
jobDO.setBeanName(entity.getBeanName());
jobDO.setMethodName(entity.getMethodName());
jobDO.setParams(entity.getParams());
jobDO.setCronExpression(entity.getCronExpression());
jobDO.setUpdateBy(LoginUtil.getLoginUser().getRealname());
jobDO.setUpdateTime(LocalDateTime.now());
int count = quartzJobMapper.update(jobDO, new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobId, entity.getJobId()));
ScheduleUtils.updateScheduleJob(entity);
return count > 0;
}
/**
* 批量删除任务
*
* @param id 任务主键数组
* @return 删除数量
*/
@Override
public int batchRemoveQuartzJob(Long[] id) {
for (Long jobId : id) {
ScheduleUtils.deleteScheduleJob(jobId);
}
return quartzJobMapper.deleteBatchIds(Arrays.asList(id));
}
/**
* 立即运行任务
*
* @param id 任务主键数组
*/
@Override
public void run(Long[] id) {
for (Long jobId : id) {
ScheduleUtils.run(quartzJobMapper.selectById(jobId));
}
}
/**
* 暂停任务
*
* @param id 任务主键数组
* @return 是否成功
*/
@Override
public boolean pause(Long[] id) {
for (Long jobId : id) {
ScheduleUtils.pauseJob(jobId);
}
QuartzJob QuartzJob = new QuartzJob();
QuartzJob.setStatus(QuartzJobStatusEnum.PAUSE.getValue());
LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
wrapper.in(id.length > 0, QuartzJob::getJobId, id);
int updates = quartzJobMapper.update(QuartzJob, wrapper);
return updates > 0;
}
/**
* 恢复任务
*
* @param id 任务主键数组
* @return 是否成功
*/
@Override
public boolean resume(Long[] id) {
for (Long jobId : id) {
ScheduleUtils.resumeJob(jobId);
}
QuartzJob QuartzJob = new QuartzJob();
QuartzJob.setStatus(QuartzJobStatusEnum.NORMAL.getValue());
LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
wrapper.in(id.length > 0, QuartzJob::getJobId, id);
int updates = quartzJobMapper.update(QuartzJob, wrapper);
return updates > 0;
}
}
任务日程对象:
public class ScheduleJob extends QuartzJobBean {
private ExecutorService service = Executors.newSingleThreadExecutor();
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
String jobStr = context.getMergedJobDataMap().getString(QuartzJob.JOB_PARAM_KEY);
QuartzJob scheduleJob = JsonUtil.parse(jobStr, QuartzJob.class);
//任务开始时间
long startTime = System.currentTimeMillis();
try {
//执行任务
log.info("任务准备执行,任务ID:" + scheduleJob.getJobId());
ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getBeanName(),
scheduleJob.getMethodName(), scheduleJob.getParams());
Future<?> future = service.submit(task);
future.get();
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
//如果是单次定时任务,执行完毕后删除该任务
if (new CronExpression(scheduleJob.getCronExpression()).getTimeAfter(new Date())==null){
//任务状态 0:失败 1:成功 2:已执行
scheduleJob.setStatus(2);
QuartzJobMapper quartzJobMapper = (QuartzJobMapper) SpringUtils.getBean("quartzJobMapper");
quartzJobMapper.updateById(scheduleJob);
ScheduleUtils.deleteScheduleJob(scheduleJob.getJobId());
}
log.info("任务执行完毕,任务ID:" + scheduleJob.getJobId() + " 总共耗时:" + times + "毫秒");
} catch (Exception e) {
log.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e);
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
jobLog.setTimes((int)times);
}finally {
//这里可以增加日志执行记录quartzJobLogMapper.save(jobLog);
}
}
日程工具类(ScheduleUtils):
@DependsOn("springUtils")
public class ScheduleUtils {
private static Scheduler scheduler = (Scheduler) SpringUtils.getBean("scheduler");
private final static String JOB_NAME = "TASK_";
/**
* 获取触发器key
*
* @param jobId 任务主键
* @return 触发器Key
*/
public static TriggerKey getTriggerKey(Long jobId) {
return TriggerKey.triggerKey(JOB_NAME + jobId);
}
/**
* 获取jobKey
*
* @param jobId 任务主键
* @return 任务Key
*/
public static JobKey getJobKey(Long jobId) {
return JobKey.jobKey(JOB_NAME + jobId);
}
/**
* 获取表达式触发器
*
* @param jobId 任务主键
* @return 触发器
*/
public static CronTrigger getCronTrigger(Long jobId) {
try {
return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
} catch (SchedulerException e) {
throw new ServiceException(ErrorEnum.CRON_TRIGGER_NOT_FOUND_EXCEPTION);
}
}
/**
* 创建定时任务
*
* @param scheduleJob 定时任务对象
*/
public static void createScheduleJob(QuartzJob scheduleJob) {
try {
/*当前cron表达式至少会执行一次*/
if (new CronExpression(scheduleJob.getCronExpression()).getTimeAfter(new Date()) != null) {
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getJobId())).build();
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder).build();
//放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(SasQuartzJobDTO.JOB_PARAM_KEY, JsonUtil.stringify(scheduleJob));
//scheduler.clear();
scheduler.scheduleJob(jobDetail, trigger);
//暂停任务
if (scheduleJob.getStatus() == QuartzJobStatusEnum.PAUSE.getValue()) {
pauseJob(scheduleJob.getJobId());
}
} else {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CREATE_EXCEPTION);
}
} catch (SchedulerException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CREATE_EXCEPTION);
} catch (ParseException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CRON_EXPRESSION_PARSE_EXCEPTION);
}
}
/**
* 更新定时任务
*
* @param scheduleJob 定时任务对象
*/
public static void updateScheduleJob(SasQuartzJobDTO scheduleJob) {
try {
if (new CronExpression(scheduleJob.getCronExpression()).getTimeAfter(new Date()) != null) {
TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = getCronTrigger(scheduleJob.getJobId());
//如果生成的trigger只是执行单次的,那么在执行完一次以后则会找不到该trigger,这个时候会出现空指针异常
if (trigger == null) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_TRIGGER_NULL_EXCEPTION);
}
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//参数
trigger.getJobDataMap().put(SasQuartzJobDTO.JOB_PARAM_KEY, JsonUtil.stringify(scheduleJob));
scheduler.rescheduleJob(triggerKey, trigger);
//暂停任务
if (scheduleJob.getStatus() == QuartzJobStatusEnum.PAUSE.getValue()) {
pauseJob(scheduleJob.getJobId());
}
}
} catch (SchedulerException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_UPDATE_EXCEPTION);
} catch (ParseException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CRON_EXPRESSION_PARSE_EXCEPTION);
}
}
/**
* 立即执行任务
*
* @param scheduleJob 定时任务对象
*/
public static void run(SasQuartzJobDO scheduleJob) {
try {
//参数
JobDataMap dataMap = new JobDataMap();
dataMap.put(SasQuartzJobDTO.JOB_PARAM_KEY, JsonUtil.stringify(scheduleJob));
scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
} catch (SchedulerException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_RUN_EXCEPTION);
}
}
/**
* 暂停任务
*
* @param jobId 任务主键
*/
public static void pauseJob(Long jobId) {
try {
scheduler.pauseJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_PAUSE_EXCEPTION);
}
}
/**
* 恢复任务
*
* @param jobId 任务主键
*/
public static void resumeJob(Long jobId) {
try {
scheduler.resumeJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_RECOVERY_EXCEPTION);
}
}
/**
* 删除定时任务
*
* @param jobId 任务主键
*/
public static void deleteScheduleJob(Long jobId) {
try {
scheduler.deleteJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new ServiceException(ErrorEnum.SCHEDULE_JOB_DELETE_EXCEPTION);
}
}
}
通过以上的代码 我们就可以实现通过QuartzJobService来管理我们的定时任务了,可以动态的添加定时任务,删除,暂停,及批量执行定时任务等功能。
当然 上面的代码并不完整,这里还缺少Quartz自带的11张基础表,当然这些表可以在quartz的jar包中找到,这里由于篇幅原因就不详细展开了。
下面我们在介绍一些跟定时任务相关的内容:cron表达式,我们在使用QuartzJobService创建定时任务时,必不可少的东西
Cron表达式
Cron-Expressions用于配置 CronTrigger 的实例。Cron 表达式实际上是由七个子表达式组成的字符串,它们描述了计划的各个细节。这些子表达式用空格分隔,代表:
- 秒
- 分
- 时
- 日
- 月
- 星期几
- 年份(可选字段)
完整的 cron-expression 的一个示例是字符串*“0 0 12 ? * WED”* ——意思是“每周三中午 12:00:00”。
单个子表达式可以包含范围和或列表
例如,上一个示例中的星期几字段(读作“WED”)可以替换为“MON-FRI”、“MON,WED,FRI”,甚至“MON-WED,SAT”。
通配符(’ * ’ 字符)可用于表示该字段的“每个”可能值。
因此,前面示例中“Month”字段中的 * 字符仅表示“每个月”。因此,Day-Of-Week 字段中的 “*” 显然表示 “一周中的每一天”。
所有字段都有一组可以指定的有效值。
这些值应该相当明显 - 例如:
- 秒和分钟的数字 0 到 59,
- 小时的值 0 到 23。
- Day-of-Month 可以是 1-31 之间的任何值,但您需要注意给定月份有多少天!
- 月份可以指定为 0 到 11 之间的值,或者使用字符串 JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV 和 DEC。
- 可以将星期几指定为 1 到 7 之间的值(1 = 星期日)或使用字符串 SUN、MON、TUE、WED、THU、FRI 和 SAT。
‘/’ 字符可用于指定值的增量。
例如,如果您在“分钟”字段中输入“0/15”,则表示“每小时的第 15 分钟,从第 0 分钟开始”。
如果您在“分钟”字段中使用“3/20”,则表示“每小时的第 20 分钟,从第三分钟开始”——换句话说,它与在“分钟”字段中指定“3、23、43”相同场地。
请注意“ /35”并不意味着“每 35 分钟”的微妙之处 - 它意味着“每小时的第 35 分钟,从零分钟开始” - 或者换句话说与指定“0,35”相同。
'?'字符为日期和星期几字段允的许字符。
它用于指定“没有特定值”。当您需要在两个字段之一而不是另一个字段中指定某些内容时,这很有用。请参阅下面的示例(和 CronTrigger JavaDoc)进行说明。
“L”字符可用于日期和星期几字段。
该字符是“last”的简写,但在两个字段中具有不同的含义。
例如,day-of-month 字段中的值“L”表示“该月的最后一天”- 1 月的第 31 天,非闰年的 2 月的第 28 天。
如果单独用于星期几字段,它仅表示“7”或“SAT”。
但如果在星期几字段中用在另一个值之后,则表示“该月的最后一个 xxx 日”——例如“6L”或“FRIL”均表示“该月的最后一个星期五”。
您还可以指定从该月最后一天开始的偏移量,例如“L-3”,这表示该日历月的倒数第三天。
使用“L”选项时,重要的是不要指定列表或值范围,因为您会得到令人困惑/意外的结果。
‘W’ 用于指定最接近给定日期的工作日(周一至周五)。
例如,如果您指定“15W”作为日期字段的值,则含义是:“离该月 15 号最近的工作日”。
‘#’ 用于指定该月的“第 n 个”XXX 工作日。
例如,星期几字段中的“6#3”或“FRI#3”的值表示“该月的第三个星期五”。
这里有更多表达式及其含义的示例 - 您可以在 org.quartz.CronExpression 的 JavaDoc 中找到更多
示例 Cron 表达式
CronTrigger 示例 1 - 用于创建每 5 分钟触发一次的触发器的表达式
“0 0/5 * * * ?”
CronTrigger 示例 2 - 创建触发器的表达式,该触发器每 5 分钟触发一次,在该分钟后的 10 秒(即上午 10:00:10、上午 10:05:10 等)触发。
“10 0/5 * * * ?”
CronTrigger 示例 3 - 用于创建触发器的表达式,该触发器在每周三和周五的 10:30、11:30、12:30 和 13:30 触发。
“0 30 10-13 ?* 周三、周五”
CronTrigger 示例 4 - 创建触发器的表达式,该触发器在每月 5 号和 20 号的上午 8 点到 10 点之间每半小时触发一次。请注意,触发器不会在上午 10:00 触发,仅在 8:00、8:30、9:00 和 9:30 触发
“0 0/30 8-9 5,20 * ?”
请注意,某些调度要求过于复杂,无法用单个触发器来表达——例如“上午 9:00 到 10:00 之间每 5 分钟一次,下午 1:00 到 10:00 之间每 20 分钟一次”。这种情况下的解决方案是简单地创建两个触发器,并注册它们以运行同一个作业。