在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置。
一、Quartz简介:
a. Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。
b. Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。
c. Quartz 允许程序开发人员根据时间的间隔来调度作业。
d. Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。
二、Quartz 核心概念
1、Job
表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:void execute(JobExecutionContext context)
2、JobDetail
表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
3、Trigger
代表一个调度参数的配置,什么时候去调。
4、Scheduler
代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
三、代码Demo
1、代码架构

2、添加依赖
<!-- quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
3、application.yml
server:
port: 8080
max-http-header-size: 10000000
tomcat:
max-http-post-size: -1
mybatis-plus:
mapper-locations:
- classpath:mapper/*/*.xml
- classpath*:mapper/*/*/*.xml
type-aliases-package: top.lrhuai.timer.*.entity
spring:
datasource:
sql-script-encoding: UTF-8
driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test?useUnicode=true
&characterEncoding=utf-8&useSSL=false
&autoReconnect=true&serverTimezone=Hongkong
username: root
password: root
thymeleaf:
mode: LSF
encoding: UTF-8
cache: false
servlet:
content-type: text/html
quartz:
job-store-type: jdbc
4、任务Job
/*** Job 的实例要到该执行它们的时候才会实例化出来。每次 Job 被执行,
* 一个新的 Job 实例会被创建。
* 其中暗含的意思就是你的 Job 不必担心线程安全性,
* 因为同一时刻仅有一个线程去执行给定 Job 类的实例,
* 甚至是并发执行同一 Job 也是如此。
* @DisallowConcurrentExecution 保证上一个任务执行完后,
* 再去执行下一个任务
*/
@DisallowConcurrentExecution
public class HelloJob implements Job, Serializable {
private static Logger logger = LoggerFactory
.getLogger(HelloJob.class);
@Autowired
private Scheduler scheduler;
public void execute(JobExecutionContext context)
throws JobExecutionException{
try {logger.info("Hello Job执行时间: " + new Date()+"
Blog:"+context.getJobDetail().getJobDataMap()
.get("blog"));
Thread.sleep(1000 * 5);
System.out.println("=======执行完成=======");
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、QuartzController
/**
* <p>
* 前端控制器
* </p>
*
* @author 梁少锋
* @since 2019-8-25
*/
@RestController
@RequestMapping("/task/quartz")
public class QuartzController {private Logger LOGGER = LoggerFactory
.getLogger(this.getClass());
@Autowired
private Scheduler scheduler;
@Autowired
private IQuartzService quartzService;
@PostMapping("/add")
public Result save(Quartz quartz){
LOGGER.info("新增任务");
try {
List<Quartz> list = quartzService.list(
new QueryWrapper<Quartz>().lambda().eq(Quartz::getJobGroup,
quartz.getJobGroup())
.eq(Quartz::getJobName,
quartz.getJobName()));
if(list != null && list.size() > 0){return Result.error
(ApiResultEnum.TRIGGER_GROUP_AND_NAME_SAME);
}System.out.println("Quartz="+ JSON
.toJSONString(quartz));
quartz.setCreateTime(LocalDateTime.now());
quartz.setJobStatus(JobStatus.STOP.getStatus());
quartzService.save(quartz);
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
@GetMapping("/list")
public Result list(QuartzDTO dto){
LOGGER.info("任务列表");
return Result.ok(quartzService.getQuartzPage(dto));
}
@PostMapping("/start")
public Result start(Long id) throws Exception {
LOGGER.info("任务列表");
Quartz quartz = quartzService.getById(id);
//如果现在任务状态等于启动状态,表示已经启动,不能再启动
if(quartz.getJobStatus().equals(JobStatus.RUN.getStatus())){
return Result.error(ApiResultEnum.TASK_IS_RUNING);}else if(quartz.getJobStatus()
.equals(JobStatus.PAUSED.getStatus())){
//现在任务状态等于停止状态,表示任务处于停止状态,只能继续执行
return Result.error(ApiResultEnum.TASK_IS_PAUSE);
}
//启动任务
addJob(quartz);
quartz.setJobStatus(JobStatus.RUN.getStatus());
quartzService.updateById(quartz);
return Result.ok();
}
/**
* 启动任务
* @param quartz
* @throws Exception
*/
public void addJob(Quartz quartz) throws Exception {
Class cls = Class.forName(quartz.getJobClassName()) ;
cls.newInstance();
//构建job信息
JobDetail job = JobBuilder.newJob(cls).withIdentity(quartz.getJobName(),
quartz.getJobGroup())
.withDescription(quartz.getDescription())
.build();
// 触发时间点
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
.cronSchedule(quartz.getCronExpression());
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger_"+quartz.getJobName(),
quartz.getJobGroup())
.withDescription(quartz.getDescription())
.startNow()
.withSchedule(cronScheduleBuilder)
.build();
//交由Scheduler安排触发
scheduler.scheduleJob(job, trigger);
}
/**
* 编辑
* @param quartz
* @return
* @throws SchedulerException
*/
@PostMapping("/edit")
public Result edit(Quartz quartz) throws SchedulerException {
LOGGER.info("任务列表");
//获取修改前的任务组名,停止移除触发器,再进行移除旧的任务
if(quartz.getOldJobGroup()!=null){
TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getOldJobName()
, quartz.getOldJobGroup());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);JobKey key = new JobKey(quartz.getOldJobName()
,quartz.getOldJobGroup());
scheduler.deleteJob(key);
System.out.println("移除任务:"+JSON.toJSONString(key));
}
quartz.setJobStatus(JobStatus.STOP.getStatus());
quartz.setModifyTime(LocalDateTime.now());
quartzService.updateById(quartz);
return Result.ok();
}
@GetMapping(value="/query")
@ResponseBody
public Result query(Long id) throws Exception {
return quartzService.getDetail(id);
}
@PostMapping("/trigger")public Result trigger(Quartz quartz,
HttpServletResponse response) {
LOGGER.info("触发任务");
try {JobKey key = new JobKey(quartz.getJobName()
,quartz.getJobGroup());
scheduler.triggerJob(key);
} catch (SchedulerException e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
@PostMapping("/pause")
public Result pause(Long id) {
LOGGER.info("停止任务");
try {
Quartz quartz = quartzService.getById(id);
if(quartz == null){
return Result.error("操作异常");
}
//判断当前任务状态是否处于停止状态(是,则恢复执行)if(JobStatus.PAUSED.getStatus()
.equals(quartz.getJobStatus())){
//停止则恢复JobKey key = new JobKey(quartz.getJobName()
,quartz.getJobGroup());
scheduler.resumeJob(key);
quartz.setJobStatus(JobStatus.RUN.getStatus());}else if(JobStatus.RUN.getStatus()
.equals(quartz.getJobStatus())){
//判断当前任务状态是否处于执行状态(是,则停止)JobKey key = new JobKey(quartz.getJobName()
,quartz.getJobGroup());
scheduler.pauseJob(key);
quartz.setJobStatus(JobStatus.PAUSED.getStatus());
}else{return Result.error
(ApiResultEnum.TASK_NOT_RUNING);
}
quartzService.updateById(quartz);
} catch (SchedulerException e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
@PostMapping("/remove")
public Result remove(Long id) {
LOGGER.info("移除任务");
try {
Quartz quartz = quartzService.getById(id);
if(quartz == null){
return Result.error("操作异常");
}TriggerKey triggerKey = TriggerKey
.triggerKey(quartz.getJobName(), quartz.getJobGroup());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务scheduler.deleteJob(JobKey
.jobKey(quartz.getJobName(), quartz.getJobGroup()));
System.out.println("removeJob:"
+JobKey.jobKey(quartz.getJobName()));
quartzService.removeById(id);
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return Result.ok();
}
}
6、运行效果

7、动态定时任务总结
1.新增
新增任务,存进数据库(默认不启动状态,停止!)
2.启动
判断任务状态,再执行启动操作
启动完毕后,通过任务id修改任务信息
3.编辑
获取修改前的任务组名,停止移除触发器,再进行移除旧的任务
然后再通过任务id修改数据库数据
4.停止和继续
判断任务状态,进行停止恢复,最后通过任务id更新任务数据
5.删除
停止移除触发器,再删除任务,再通过id删除数据库数据
加油!!!互相学习交流!




