暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SpringBoot整合Quartz实现动态定时任务

熊猫锋哥哥 2019-09-26
830


在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置


一、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.Driver

url: 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删除数据库数据



加油!!!互相学习交流!

文章转载自熊猫锋哥哥,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论