场景描述 {#%E5%9C%BA%E6%99%AF%E6%8F%8F%E8%BF%B0}
在项目的实际场景中,我们经常会遇到一些任务需要每天、每周、或者固定时间去执行,所以在项目中加入Quartz框架,来更好的对这些事情做管理,只需要配置任务对应的CORN表达式,添加到任务里面即可让他自动化的实现对任务的管理。
集成教程 {#%E9%9B%86%E6%88%90%E6%95%99%E7%A8%8B}
- 项目POM文件中引入依赖 {#1.-%E9%A1%B9%E7%9B%AEpom%E6%96%87%E4%BB%B6%E4%B8%AD%E5%BC%95%E5%85%A5%E4%BE%9D%E8%B5%96}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 在项目
application.properties
中新增如下配置 {#2.-%E5%9C%A8%E9%A1%B9%E7%9B%AEapplication.properties%E4%B8%AD%E6%96%B0%E5%A2%9E%E5%A6%82%E4%B8%8B%E9%85%8D%E7%BD%AE}
注意:
1、如果需要quartz 第一次运行时自动生成 quartz 所需的表那么 quartzJob? 后面的配置为 :allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
待第一次运行后可以再根据自己的需要修改
2、配置文件中的 initialize-schema: always 配置的 always 属性意思是,每次初始化都会重新生成表(执行一次删除,执行一次创建),生成后,可以修改为 never
只有以上两个条件同时配置满足,才能使quartz 在第一次运行时,自动生成所需的表
# quartz定时任务,采用数据库方式 如果需要quartz 第一次运行时自动生成 quartz 所需的表那么 quartzJob? 后面的配置为 :allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai spring.quartz.job-store-type=jdbc # ?配置文件中的 initialize-schema: always 配置的 always 属性意思是,每次初始化都会重新生成表(执行一次删除,执行一次创建),生成后,可以修改为 never spring.quartz.jdbc.initialize-schema=never
时任务启动开关,true-开 false-关
spring.quartz.auto-startup=true #??1??????? spring.quartz.startup-delay=1s
spring.quartz.overwrite-existing-jobs=true
Quartz Scheduler Properties
spring.quartz.properties.org.quartz.scheduler.instanceName = MyScheduler spring.quartz.properties.org.quartz.scheduler.instanceId = AUTO
spring.quartz.properties.org.quartz.jobStore.class = org.springframework.scheduling.quartz.LocalDataSourceJobStore spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
??????
spring.quartz.properties.org.quartz.jobStore.tablePrefix = QRTZ_ spring.quartz.properties.org.quartz.jobStore.isClustered = true spring.quartz.properties.org.quartz.jobStore.misfireThreshold = 12000 spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval = 15000
????????
spring.quartz.properties.org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool spring.quartz.properties.org.quartz.threadPool.threadCount = 1 spring.quartz.properties.org.quartz.threadPool.threadPriority = 5 spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
上面配置好之后,启动项目会直接在对应链接的数据库下生成11张默认的表,均是以QRTZ开头,如下图 {#%E4%B8%8A%E9%9D%A2%E9%85%8D%E7%BD%AE%E5%A5%BD%E4%B9%8B%E5%90%8E%EF%BC%8C%E5%90%AF%E5%8A%A8%E9%A1%B9%E7%9B%AE%E4%BC%9A%E7%9B%B4%E6%8E%A5%E5%9C%A8%E5%AF%B9%E5%BA%94%E9%93%BE%E6%8E%A5%E7%9A%84%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8B%E7%94%9F%E6%88%9011%E5%BC%A0%E9%BB%98%E8%AE%A4%E7%9A%84%E8%A1%A8%EF%BC%8C%E5%9D%87%E6%98%AF%E4%BB%A5qrtz%E5%BC%80%E5%A4%B4%EF%BC%8C%E5%A6%82%E4%B8%8B%E5%9B%BE}
- 在生成的表上我们还需要新增一张自己添加任务的配置表,具体如下 {#3.-%E5%9C%A8%E7%94%9F%E6%88%90%E7%9A%84%E8%A1%A8%E4%B8%8A%E6%88%91%E4%BB%AC%E8%BF%98%E9%9C%80%E8%A6%81%E6%96%B0%E5%A2%9E%E4%B8%80%E5%BC%A0%E8%87%AA%E5%B7%B1%E6%B7%BB%E5%8A%A0%E4%BB%BB%E5%8A%A1%E7%9A%84%E9%85%8D%E7%BD%AE%E8%A1%A8%EF%BC%8C%E5%85%B7%E4%BD%93%E5%A6%82%E4%B8%8B}
CREATE TABLE `sys_quartz_job` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`del_flag` int(11) DEFAULT NULL COMMENT '删除状态',
`update_by` varchar(32) DEFAULT NULL COMMENT '修改人',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`job_class_name` varchar(255) DEFAULT NULL COMMENT '任务类名',
`cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron表达式',
`parameter` varchar(255) DEFAULT NULL COMMENT '参数',
`meeting_record_id` int(11) DEFAULT NULL COMMENT '会议室记录id',
`description` varchar(255) DEFAULT NULL COMMENT '描述',
`status` int(11) DEFAULT NULL COMMENT '状态 0正常 -1停止',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
- 添加代码,补充Quartz的功能 {#4.-%E6%B7%BB%E5%8A%A0%E4%BB%A3%E7%A0%81%EF%BC%8C%E8%A1%A5%E5%85%85quartz%E7%9A%84%E5%8A%9F%E8%83%BD}
此处功能是可以实现对定时任务的管理,比如添加、删除、重新配置、立即执行定时任务等。
Controller类 {#controller%E7%B1%BB}
// Controller类
/**
@Description: 定时任务 */ @RestController @RequestMapping("/sys/quartzJob") @Slf4j @Api(tags = "定时任务接口") public class QuartzJobController { @Autowired private QuartzJobService quartzJobService; @Autowired private Scheduler scheduler;
/**
分页列表查询
@param quartzJob
@param page
@param pageSize
@return */ @ApiOperation(value = "分页列表查询", notes = "分页列表查询") @RequestMapping(value = "/list", method = RequestMethod.GET) public ApiResult queryPageList(QuartzJob quartzJob, @RequestParam(value = "page", defaultValue = "1") int page, @RequestParam(value = "pageSize", defaultValue = "20") int pageSize) { Page<QuartzJob> list = quartzJobService.selectList(quartzJob,page,pageSize); ApiResult apiResult = new ApiResult(); if (CollectionUtils.isNotEmpty(list.getItems())){ apiResult.setData(list); } return ApiResult.ok("list",list);
}
/**
添加定时任务
@param quartzJob
@return */ //@RequiresRoles("admin") @ApiOperation(value = "添加定时任务", notes = "添加定时任务") @RequestMapping(value = "/add", method = RequestMethod.POST) public ApiResult add(@RequestBody QuartzJob quartzJob) { boolean b = quartzJobService.saveAndScheduleJob(quartzJob); if (b == true) { return ApiResult.ok("add"); } return ApiResult.fail("add","添加定时任务失败"); }
/**
更新定时任务
@param quartzJob
@return */ //@RequiresRoles("admin") @ApiOperation(value = "更新定时任务", notes = "更新定时任务") @RequestMapping(value = "/edit", method ={RequestMethod.PUT, RequestMethod.POST}) public ApiResult eidt(@RequestBody QuartzJob quartzJob) { try { quartzJobService.editAndScheduleJob(quartzJob); } catch (SchedulerException e) { log.error(e.getMessage(),e); return ApiResult.fail("edit","更新定时任务失败!"); } return ApiResult.ok("edit"); }
/**
通过id删除
@param id
@return */ @ApiOperation(value = "通过id删除", notes = "通过id删除") @RequestMapping(value = "/delete", method = RequestMethod.DELETE) public ApiResult delete(@RequestParam(name = "id", required = true) String id) { QuartzJob quartzJob = quartzJobService.getById(Long.valueOf(id)); if (quartzJob == null) { return ApiResult.fail("delete","未找到对应实体"); } quartzJobService.deleteAndStopJob(Long.valueOf(id)); return ApiResult.ok("delete");
}
/**
批量删除
@param ids
@return */ @ApiOperation(value = "批量删除", notes = "批量删除") @RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE) public ApiResult deleteBatch(@RequestParam(name = "ids", required = true) String ids) { if (ids == null || "".equals(ids.trim())) { return ApiResult.fail("deleteBatch","参数不识别!"); } for (String id : Arrays.asList(ids.split(","))) { QuartzJob job = quartzJobService.getById(Long.valueOf(id)); quartzJobService.deleteAndStopJob(Long.valueOf(id)); } return ApiResult.ok("deleteBatch"); }
/**
暂停定时任务
@param id
@return */ @GetMapping(value = "/pause") @ApiOperation(value = "停止定时任务") public ApiResult pauseJob(@RequestParam(name = "id") String id) { QuartzJob job = quartzJobService.getById(Long.valueOf(id)); if (job == null) { return ApiResult.fail("pause","定时任务不存在!"); } quartzJobService.pause(job); return ApiResult.ok("pause"); }
/**
启动定时任务
@param id
@return */ @GetMapping(value = "/resume") @ApiOperation(value = "启动定时任务") public ApiResult resumeJob(@RequestParam(name = "id") String id) { QuartzJob job = quartzJobService.getById(Long.valueOf(id)); if (job == null) { return ApiResult.fail("resume","定时任务不存在!"); } quartzJobService.resumeJob(job); //scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim())); return ApiResult.ok("resume"); }
/**
通过id查询
@param id
@return */ @ApiOperation(value = "通过id查询", notes = "通过id查询") @RequestMapping(value = "/queryById", method = RequestMethod.GET) public ApiResult queryById(@RequestParam(name = "id", required = true) String id) { QuartzJob quartzJob = quartzJobService.getById(Long.valueOf(id)); ApiResult apiResult = new ApiResult(); apiResult.setData(quartzJob); return apiResult; }
/**
立即执行
@param id
@return */ @ApiOperation(value = "立即执行", notes = "立即执行") @GetMapping("/execute") public ApiResult execute(@RequestParam(name = "id", required = true) String id) { QuartzJob quartzJob = quartzJobService.getById(Long.valueOf(id)); if (quartzJob == null) { return ApiResult.fail("execute","未找到对应实体"); } try { quartzJobService.execute(quartzJob); } catch (Exception e) { //e.printStackTrace(); log.info("定时任务 立即执行失败>>"+e.getMessage()); return ApiResult.fail("execute","执行失败!"); } return ApiResult.ok("execute"); }
}
Service类 {#service%E7%B1%BB}
@Service @Slf4j public class QuartzJobService{
@Resource private QuartzJobMapper quartzJobMapper; @Autowired private Scheduler scheduler;
/**
- 立即执行的任务分组 */ private static final String JOB_TEST_GROUP = "test_group";
public Page&lt;QuartzJob&gt; selectList(QuartzJob quartzJob, int page, int pageSize) { Page&lt;QuartzJob&gt; pageQuartzJobVo = Page.create(page, pageSize); Example example = new Example(QuartzJob.class); Example.Criteria criteria = example.createCriteria(); if (!StringUtils.isEmpty(quartzJob.getJobClassName())) { criteria.andLike("jobClassName","%"+ quartzJob.getJobClassName() +"%"); } if (!StringUtils.isEmpty(quartzJob.getCronExpression())) { criteria.andEqualTo("cronExpression", quartzJob.getCronExpression()); } if (!StringUtils.isEmpty(quartzJob.getDescription())) { criteria.andLike("", "%"+quartzJob.getDescription() +"%"); } // 查询未删除的定时列表 criteria.andEqualTo("delFlag",CommonConstant.DEL_FLAG_0.getCode()); List&lt;QuartzJob&gt; quartzJobs = quartzJobMapper.selectByExample(example); pageQuartzJobVo.setItems(quartzJobs); pageQuartzJobVo.setTotalCount(quartzJobs.size()); return pageQuartzJobVo; }
public boolean saveAndScheduleJob(QuartzJob quartzJob) { // DB设置修改 quartzJob.setDelFlag(CommonConstant.DEL_FLAG_0.getCode()); boolean success = this.save(quartzJob); if (success) { if (CommonConstant.STATUS_NORMAL.getCode().equals(quartzJob.getStatus())) { // 定时器添加 this.schedulerAdd(String.valueOf(quartzJob.getId()), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter()); } } return success; }
private boolean save(QuartzJob quartzJob) { if (StringUtils.isEmpty(quartzJob.getCreateBy())){ quartzJob.setCreateBy("admin"); } if (StringUtils.isEmpty(quartzJob.getCreateTime())){ quartzJob.setCreateTime(new Date()); } int i = quartzJobMapper.insertSelective(quartzJob); if (i == 1){ return true; } return false; }
public void editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException { if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) { schedulerDelete(quartzJob.getId()); schedulerAdd(String.valueOf(quartzJob.getId()), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter()); }else{ scheduler.pauseJob(JobKey.jobKey(String.valueOf(quartzJob.getId()))); } this.updateById(quartzJob); }
private void updateById(QuartzJob quartzJob) { QuartzJob q = new QuartzJob(); q.setId(quartzJob.getId()); if (!StringUtils.isEmpty(quartzJob.getCronExpression())){ q.setCronExpression(quartzJob.getCronExpression()); } if (!StringUtils.isEmpty(quartzJob.getJobClassName())){ q.setJobClassName(quartzJob.getJobClassName()); } if (!StringUtils.isEmpty(quartzJob.getDescription())){ q.setDescription(quartzJob.getDescription()); } if (!StringUtils.isEmpty(quartzJob.getDelFlag())){ q.setDelFlag(quartzJob.getDelFlag()); } if (!StringUtils.isEmpty(quartzJob.getStatus())){ q.setStatus(quartzJob.getStatus()); } if (!StringUtils.isEmpty(quartzJob.getParameter())){ q.setParameter(quartzJob.getParameter()); } q.setUpdateBy("admin"); q.setUpdateTime(new Date()); quartzJobMapper.updateByPrimaryKeySelective(q); }
/**
添加定时任务
@param jobClassName
@param cronExpression
@param parameter */ private void schedulerAdd(String id, String jobClassName, String cronExpression, String parameter) { try { // 启动调度器 scheduler.start();
// 构建job信息 JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(id).usingJobData(&quot;parameter&quot;, parameter).build(); // 表达式调度构建器(即任务执行的时间) CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression); // 按新的cronExpression表达式构建一个新的trigger CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(id).withSchedule(scheduleBuilder).build(); scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) { throw new BusinessException("创建定时任务失败" + e); } catch (RuntimeException e) { throw new BusinessException(e.getMessage() + e); }catch (Exception e) { throw new BusinessException("后台找不到该类名:" + jobClassName + e); } }
/**
- (删除&amp;停止)删除定时任务 */ public boolean deleteAndStopJob(Long id) { schedulerDelete(id); QuartzJob quartzJob = new QuartzJob(); quartzJob.setId(id); quartzJob.setDelFlag(CommonConstant.DEL_FLAG_1.getCode()); quartzJob.setStatus(CommonConstant.STATUS_UNNORMAL.getCode()); int i = quartzJobMapper.updateByPrimaryKeySelective(quartzJob); if (i == 1){ return true; } return false; }
/**
- 删除定时任务
- @param id */ private void schedulerDelete(Long id) { try { scheduler.pauseTrigger(TriggerKey.triggerKey(String.valueOf(id))); scheduler.unscheduleJob(TriggerKey.triggerKey(String.valueOf(id))); scheduler.deleteJob(JobKey.jobKey(String.valueOf(id))); } catch (Exception e) { log.error(e.getMessage(), e); throw new BusinessException("删除定时任务失败"); } }
private static Job getClass(String classname) throws Exception { Class&lt;?&gt; class1 = Class.forName(classname); return (Job) class1.newInstance(); }
public QuartzJob getById(Long id) { QuartzJob quartzJob = new QuartzJob(); quartzJob.setId(id); quartzJob.setStatus(CommonConstant.STATUS_NORMAL.getCode()); QuartzJob quartzJob1 = quartzJobMapper.selectOne(quartzJob); return quartzJob1; }
public void pause(QuartzJob quartzJob){ schedulerDelete(quartzJob.getId()); quartzJob.setStatus(CommonConstant.STATUS_UNNORMAL.getCode()); this.updateById(quartzJob); }
public void resumeJob(QuartzJob quartzJob) { schedulerDelete(quartzJob.getId()); schedulerAdd(String.valueOf(quartzJob.getId()), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter()); quartzJob.setStatus(CommonConstant.STATUS_NORMAL.getCode()); this.updateById(quartzJob); }
public void execute(QuartzJob quartzJob) throws Exception { String jobName = quartzJob.getJobClassName().trim(); Date startDate = new Date(); String ymd = DateUtils.format(startDate,"yyyymmddhhmmss"); String identity = jobName + ymd; //3秒后执行 只执行一次 // update-begin--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒------- startDate.setTime(startDate.getTime() + 100L); // update-end--author:sunjianlei ---- date:20210511--- for:定时任务立即执行,延迟3秒改成0.1秒------- // 定义一个Trigger SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger() .withIdentity(identity, JOB_TEST_GROUP) .startAt(startDate) .build(); // 构建job信息 JobDetail jobDetail = JobBuilder.newJob(getClass(jobName).getClass()).withIdentity(identity).usingJobData("parameter", quartzJob.getParameter()).build(); // 将trigger和 jobDetail 加入这个调度 scheduler.scheduleJob(jobDetail, trigger); // 启动scheduler scheduler.start(); }
}
Mapper类 {#mapper%E7%B1%BB}
public interface QuartzJobMapper extends Mapper<QuartzJob> {
/** * 根据jobClassName查询 * @param jobClassName 任务类名 * @return */ public List&lt;QuartzJob&gt; findByJobClassName(@Param("jobClassName") String jobClassName);
}
实体类 {#%E5%AE%9E%E4%BD%93%E7%B1%BB}
@Data
@Table(name = "sys_quartz_job")
public class QuartzJob implements Serializable {
/**
* id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 创建人
*/
@Column(name = "create_by")
private String createBy;
/**
- 创建时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name = "create_time")
private Date createTime;
/**
- 删除状态
*/
@Column(name = "del_flag")
private Integer delFlag;
/**
- 修改人
*/
@Column(name = "update_by")
private String updateBy;
/**
- 修改时间
*/
@Column(name = "update_time")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
- 任务类名
*/
@Column(name = "job_class_name")
private String jobClassName;
/**
- cron表达式
*/
@Column(name = "cron_expression")
private String cronExpression;
/**
- 参数
*/
private String parameter;
/**
- 描述
*/
private String description;
/**
状态 0正常 -1停止
*/
private Integer status;
}
- 简单的Job任务类,这个根据自己的实际需求进行更改 {#5.-%E7%AE%80%E5%8D%95%E7%9A%84job%E4%BB%BB%E5%8A%A1%E7%B1%BB%EF%BC%8C%E8%BF%99%E4%B8%AA%E6%A0%B9%E6%8D%AE%E8%87%AA%E5%B7%B1%E7%9A%84%E5%AE%9E%E9%99%85%E9%9C%80%E6%B1%82%E8%BF%9B%E8%A1%8C%E6%9B%B4%E6%94%B9}
根据实际开发的需要,选择适合自己的任务类搭配即可实现自己想要的效果
任务类一 {#%E4%BB%BB%E5%8A%A1%E7%B1%BB%E4%B8%80}
/** * @Description: 同步定时任务测试 * * 此处的同步是指 当定时任务的执行时间大于任务的时间间隔时 * 会等待第一个任务执行完成才会走第二个任务 */ @PersistJobDataAfterExecution // 持久化JobDataMap里的数据,使下一个定时任务还能获取到这些值 @DisallowConcurrentExecution // 禁止并发多任务执行,所以永远只有一个任务在执行中 @Slf4j public class Test1Job implements Job {
@Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { log.info(" --- 同步任务调度开始 --- " + " Job Execution key:"+jobExecutionContext.getJobDetail().getKey()); try { //此处模拟任务执行时间 5秒 任务表达式配置为每秒执行一次:0/1 * * * * ? * Thread.sleep(20000); } catch (InterruptedException e) { e.printStackTrace(); } //测试发现 每5秒执行一次 log.info(" --- 执行完毕,时间:"+new Date()+"---" + " 线程名"+ Thread.currentThread().getName() ); }
}
任务类二 {#%E4%BB%BB%E5%8A%A1%E7%B1%BB%E4%BA%8C}
@Slf4j
public class Test2Job implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
log.info(String.format(" rih-health-center 普通定时任务 Test2Job ! 时间:" + new Date()));
}
}
任务类三 {#%E4%BB%BB%E5%8A%A1%E7%B1%BB%E4%B8%89}
@Slf4j public class Test3Job implements Job {
/** * 若参数变量名修改 QuartzJobController中也需对应修改 */ private String parameter;
public void setParameter(String parameter) { this.parameter = parameter; } @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info(&quot; Job Execution key:&quot;+jobExecutionContext.getJobDetail().getKey()); log.info( String.format(&quot;welcome %s! 带参数定时任务 Test3Job ! 时间:&quot; + new Date(), this.parameter));
}
}