场景描述
在项目的实际场景中,我们经常会遇到一些任务需要每天、每周、或者固定时间去执行,所以在项目中加入Quartz框架,来更好的对这些事情做管理,只需要配置任务对应的CORN表达式,添加到任务里面即可让他自动化的实现对任务的管理。
集成教程
1. 项目POM文件中引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2. 在项目application.properties
中新增如下配置
注意:
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开头,如下图
3. 在生成的表上我们还需要新增一张自己添加任务的配置表,具体如下
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;
4. 添加代码,补充Quartz的功能
此处功能是可以实现对定时任务的管理,比如添加、删除、重新配置、立即执行定时任务等。
Controller类
// 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
@Slf4j
public class QuartzJobService{
@Resource
private QuartzJobMapper quartzJobMapper;
@Autowired
private Scheduler scheduler;
/**
* 立即执行的任务分组
*/
private static final String JOB_TEST_GROUP = "test_group";
public Page<QuartzJob> selectList(QuartzJob quartzJob, int page, int pageSize) {
Page<QuartzJob> 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<QuartzJob> 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("parameter", 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);
}
}
/**
* (删除&停止)删除定时任务
*/
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<?> 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类
public interface QuartzJobMapper extends Mapper<QuartzJob> {
/**
* 根据jobClassName查询
* @param jobClassName 任务类名
* @return
*/
public List<QuartzJob> findByJobClassName(@Param("jobClassName") String jobClassName);
}
实体类
@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;
}
5. 简单的Job任务类,这个根据自己的实际需求进行更改
根据实际开发的需要,选择适合自己的任务类搭配即可实现自己想要的效果
任务类一
/**
* @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() );
}
}
任务类二
@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()));
}
}
任务类三
@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(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
log.info( String.format("welcome %s! 带参数定时任务 Test3Job ! 时间:" + new Date(), this.parameter));
}
}
评论区