说到定时任务,估计都用过。
①、比如每天15点给lilei发邮件通知等,这种可以直接使用注解@Scheduled标注在一个方法上
②、但是如果我想每天14点发邮件,那是不是得改代码再重启服务,于是我们想到表达式写到配置文件中,再重启服务,相对于前面的可能简单一点
③、那我们还有没更简单的,达到动态修改执行时间且不需要重启服务,答案是有的。也就是本文所要说的。
本文只是简单写了个demo,具体业务自行修改添加
首先pom.xml引入Quartz
org.quartz-scheduler
quartz
com.mchange
c3p0
创建数据库:
CREATE TABLE `sys_job` (
`id` varchar(64) NOT NULL COMMENT '任务ID',
`job_name` varchar(64) NOT NULL DEFAULT '' COMMENT '任务名称',
`job_group` varchar(64) NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',
`invoke_target` varchar(500) DEFAULT NULL COMMENT '调用目标字符串',
`cron_expression` varchar(255) DEFAULT '' COMMENT 'cron执行表达式',
`misfire_policy` varchar(20) DEFAULT '1' COMMENT '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
`concurrent` char(1) DEFAULT '0' COMMENT '是否并发执行(0允许 1禁止)',
`status` char(1) DEFAULT '1' COMMENT '状态(0正常 1暂停)',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT '' COMMENT '备注信息',
`full_fixed` int(2) DEFAULT NULL COMMENT '全局设置',
`cron_description` varchar(255) DEFAULT NULL COMMENT 'cron表达式描述',
`parameter` varchar(255) DEFAULT NULL COMMENT '参数,功能项',
PRIMARY KEY (`id`,`job_name`,`job_group`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='定时任务调度表';
对应实体类:
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@ApiModel(value="SysJob对象", description="定时任务调度表")
public class SysJob extends BaseEntity {
private static final long serialVersionUID=1L;
@ApiModelProperty(value = "任务名称")
private String jobName;
@ApiModelProperty(value = "任务组名")
private String jobGroup;
@ApiModelProperty(value = "调用目标字符串")
private String invokeTarget;
@ApiModelProperty(value = "cron执行表达式")
private String cronExpression;
@ApiModelProperty(value = "定时描述")
private String cronDescription;
@ApiModelProperty(value = "计划执行错误策略(1立即执行 2执行一次 3放弃执行)")
private String misfirePolicy;
@ApiModelProperty(value = "是否并发执行(0允许 1禁止)")
private String concurrent;
@ApiModelProperty(value = "状态(0正常 1暂停)")
private String status;
@ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "创建时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新者")
private String updateBy;
@ApiModelProperty(value = "更新时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@ApiModelProperty(value = "备注信息")
private String remark;
@ApiModelProperty(value = "参数功能项,即对应的指令 比如:'00000000'")
private String parameter;
//@ApiModelProperty(value = "下次执行时间")
//private LocalDateTime nextExecutionTime;
@ApiModelProperty(value = "全局定时 0-否| 1-是")
private Integer fullFixed;
}
任务工具类
public class ScheduleUtil {
/**
* 获取任务执行类
*
* @param sysJob
* @return
* @throws Exception
*/
private static Class extends Job> getQuartzJobClass(SysJob sysJob) throws Exception {
String className = sysJob.getInvokeTarget();
// boolean isConcurrent = "0".equals(sysJob.getConcurrent());
// return isConcurrent ? SampleJob.class : QuartzDisallowConcurrentExecution.class;
Class jobClass = null;
try {
if (!isValidClassName(className)) {
jobClass = (Class) SpringUtils.getBean(className);
} else {
jobClass = (Class) Class.forName(className);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new Exception("任务类不存在");
}
return jobClass;
}
/**
* 构建任务触发对象
*/
public static TriggerKey getTriggerKey(String jobId, String jobGroup) {
return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
}
/**
* 构建任务键对象
*/
public static JobKey getJobKey(String jobId, String jobGroup) {
return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
}
/**
* 创建定时任务
*/
public static void createScheduleJob(Scheduler scheduler, SysJob job) throws Exception {
Class extends Job> jobClass = getQuartzJobClass(job);
// 构建job信息
String jobId = job.getId();
String jobGroup = job.getJobGroup();
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
// 表达式调度构建器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)).withSchedule(cronScheduleBuilder).build();
// 放入参数,运行时的方法可以获取
jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
// 判断是否存在
if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(getJobKey(jobId, jobGroup));
}
scheduler.scheduleJob(jobDetail, trigger);
// // 暂停任务
// if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
// scheduler.pauseJob(getJobKey(jobId, jobGroup));
// }
}
/**
* 设置定时任务策略
*/
public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) throws Exception {
switch (job.getMisfirePolicy()) {
case ScheduleConstants.MISFIRE_DEFAULT:
return cb;
case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
return cb.withMisfireHandlingInstructionIgnoreMisfires();
case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
return cb.withMisfireHandlingInstructionFireAndProceed();
case ScheduleConstants.MISFIRE_DO_NOTHING:
return cb.withMisfireHandlingInstructionDoNothing();
default:
throw new RuntimeException("The task misfire policy '" + job.getMisfirePolicy()
+ "' cannot be used in cron schedule tasks");
}
}
public static boolean isValidClassName(String invokeTarget) {
return StringUtils.countMatches(invokeTarget, ".") > 1;
}
}
}
@Service
public class SysJobServiceImpl extends ServiceImpl implements ISysJobService {
@Autowired
private Scheduler scheduler;
/**
* 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
*/
@PostConstruct
public void init() throws Exception {
scheduler.clear();
List jobList = baseMapper.selectList(new QueryWrapper<>());
for (SysJob job : jobList) {
ScheduleUtil.createScheduleJob(scheduler, job);
}
}
/**
* 保存
*
* @param sysJob
* @return
* @throws Exception
*/
@Override
public boolean insert(SysJob sysJob) throws Exception {
boolean save = save(sysJob);
if (save) {
ScheduleUtil.createScheduleJob(scheduler, sysJob);
}
return save;
}
@Override
public boolean deleteJob(SysJob job) throws Exception {
String jobId = job.getId();
String jobGroup = job.getJobGroup();
boolean del = removeById(jobId);
if (del) {
scheduler.deleteJob(ScheduleUtil.getJobKey(jobId, jobGroup));
}
return del;
}
@Override
public boolean deleteJob(String primary) throws Exception {
SysJob sysJob = this.getBaseMapper().selectOne(new QueryWrapper<>());
return deleteJob(sysJob);
}
@Override
public void deleteByPrimarys(String[] jobIds) throws Exception {
for (String jobId : jobIds) {
SysJob job = baseMapper.selectById(jobId);
deleteJob(job);
}
}
@Override
public boolean updateByPrimary(SysJob sysJob) throws Exception {
SysJob properties = baseMapper.selectById(sysJob.getId());
boolean update = update(sysJob, new QueryWrapper<>());
if (update) {
updateSchedulerJob(sysJob, properties.getJobGroup());
}
return update;
}
/**
* @param page
* @param queryWrapper
* @描述:分页
*/
@Override
public IPage page(IPage page, QueryWrapper queryWrapper) throws Exception {
return baseMapper.selectPage(page, queryWrapper);
}
/**
* @param queryWrapper
* @描述:条件查询
*/
@Override
public List select(Wrapper queryWrapper) throws Exception {
return baseMapper.selectList(queryWrapper);
}
/**
* 更新任务
*
* @param job 任务对象
* @param jobGroup 任务组名
*/
public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, Exception {
String jobId = job.getId();
// 判断是否存在
JobKey jobKey = ScheduleUtil.getJobKey(jobId, jobGroup);
if (scheduler.checkExists(jobKey)) {
// 防止创建时存在数据问题 先移除,然后在执行创建操作
scheduler.deleteJob(jobKey);
}
ScheduleUtil.createScheduleJob(scheduler, job);
}
}
@Component("sampleJob")
public class SampleJob extends AbstractQuartzJob {
//Job的实例是在真正调度JobDetail中的Job实现类时进行实例化的,此对象由框架本身进行管理,所以实例化的对象并没有交给Spring来管理
// 需要注入的依赖bean
private static ITaskService taskService;
// 定义一个set方法
@Autowired
public void setTaskService(ITaskService taskService) {
SampleJob.taskService = taskService;
}
// private ITaskService taskService = SpringUtils.getBean( "taskService");
@Override
protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
System.out.println("start");
System.out.println("sysJob = " + sysJob);
System.out.println("end");
// String taskCode = context.getJobDetail().getKey().getName();
// //具体任务执行的业务
// taskService.command(sysJob);
}
}
最终看下测试效果
完美!