目录
2.3Quartz框架中Job依赖注入service,实现数据库操作
一、前言
这篇是学习SpringBoot的第七篇文章,这篇记录的是Quartz定时框架的使用。本次项目的工程还是延用这之前创建的firstproject工程。
SpringBoot本身有一个定时任务的框架,但是Task也有不足之处。想动态的执行定时任务就比较困难。这就体现出了quartz的优势,它很容易的管理定时任务,很容易动态的操作定时任务。
Quartz有三个核心概念:调度器、任务和触发器。三者关系是,调度器负责调度各个任务,到了某个时刻或者过了一定时间,触发器触动了,特定任务便启动执行。
这边文章相对较长,主要分为4个部分,第一部分是介绍quartz框架,第二部分是使用框架做一个简单的例子,第三部分是能够进行数据库操作,第四部分是能够动态的操作任务的执行。便于大家寻找我i将每部分的标题飘上了橘黄色。
二、开启Quartz之旅
2.1 Quartz框架介绍
Quartz框架有几大核心组件:
1.Scheduler任务调度
是Quartz最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。
使触发器及任务关联起来。(后面的代码中有详细的上下文)
scheduler.scheduleJob(jobDetail, cronTrigger);
2.Job任务,JobExecutionContext类提供调度上下文的各种信息
其实Job是接口,任务类要实现Job接口,重写Job接口的execute方法。
任务类还可以使用以下三个注解进行修饰:
@DisallowConcurrentExecution:此注解表示不允许这个Job并发执行
@PersistJobDataAfterExecution:此注解表示当这个Job的execute方法执行成功后,更新并存储它所持有的JobDetail属性中JobDataMap。如果使用这个注解,建议也使用@DisallowConcurrentExecution,因为并发执行过程中,JobDataMap有可能会发生冲突。
3.Trigger触发器/TruggerBuilder(创建Trigger)/ThreadPool(线程)
执行任务的规则;比如每天,每小时等。
一般情况使用SimpleTrigger,和CronTrigger,这些触发器实现了Trigger接口。SimpleScheduleBuilder和CronScheduleBuilder继承ScheduleBuilder抽象类 。
对于简单的时间来说,比如每天执行几次,使用SimpleTrigger。
对于复杂的时间表达式来说,比如每个月15日上午几点几分,使用CronTrigger以及CromExpression 类。
Priority:这个属性表示Trigger的权重。当两个Trigger触发时间相同时,权重大的那个先执行。Quartz默认的权重值为5。
Misfire Instruction:在Trigger接口中可以设置错过触发处理机制
4.JobDetail任务细节/JobBuilder(创建JobDetail)/JobStore保存Job数据:保存内存或数据库中)/JobDataMap:JSON数据格式
任务细节,Quartz执行Job时,需要新建个Job实例,但是不能直接操作Job类,所以通过JobDetail来获取Job的名称、描述信息。
调度器作为作业的总指挥,触发器作为作业的操作者,作业为应用的功能模块。
JobDetail有两个boolean属性。
isDurable:如果设为false,则对应的Job一旦没有关联的触发器,就会被Scheduler自动删除。 requestsRecovery:如果设为true,当Job执行中遇到硬中断(例如运行崩溃、机器断电等),Scheduler会重新执行。这种情况下,
JobExecutionContext.isRecovering()会返回ture。
5.Calendar(排除某个任务不执行)
一个Trigger可以和多个Calendar关联
6.监听器JobListener TriggerListener SchedulerListener
7.JobKey 和 TriggerKey
在Quartz中,可以分别通过JobKey和TriggerKey来唯一地识别一个Job或一个Trigger。
这两个Key都有两个关键属性:name和group。
2.2 从一个简单的demo开始
2.2.1 编写任务类,实现Job接口。我这里编写了两个任务类
Job1
package com.xuexue.firstproject.Quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class QuartzJob1 implements Job {
private void before(){
System.out.println("QuartzJob1 任务开始执行");
}
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
before();
System.out.println("开始:"+System.currentTimeMillis());
// TODO 业务
System.out.println("结束:"+System.currentTimeMillis());
after();
}
private void after(){
System.out.println("QuartzJob1 任务结束执行");
}
}
Job2
package com.xuexue.firstproject.Quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class QuartzJob2 implements Job {
private void before(){
System.out.println("QuartzJob2任务开始执行");
}
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
before();
System.out.println("开始:"+System.currentTimeMillis());
// TODO 业务
System.out.println("结束:"+System.currentTimeMillis());
after();
}
private void after(){
System.out.println("QuartzJob2任务结束执行");
}
}
2.2.2编写任务调度和处理的类.
Schedule提供了几个操作任务的方法
scheduler.start():开启job
scheduler.scheduleJob(jobDetail, cronTrigger):再容器中使触发器和任务关联
scheduler.pauseAll():暂停所有任务
scheduler.pauseJob(jobKey):暂停一个任务
scheduler.resumeAll():恢复所有任务
scheduler.resumeJob(jobKey):恢复一个任务
scheduler.deleteJob(jobKey):从容器中删除一个任务
以下是任务调度和处理的类的代码:
package com.xuexue.firstproject.Quartz;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.Date;
@Configuration
public class QuartzJobScheduler {
// 任务调度
@Autowired
private Scheduler scheduler;
/**
* 开始执行所有任务
*/
public void startJob() throws SchedulerException {
startJob1(scheduler);
startJob2(scheduler);
scheduler.start();
}
/**
* 将job1添加到容器中
*/
private void startJob1(Scheduler scheduler) throws SchedulerException {
// 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例
// JobDetail 是具体Job实例
JobDetail jobDetail = JobBuilder.newJob(QuartzJob1.class).withIdentity("job1", "group1").build();
// 基于表达式构建触发器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
// CronTrigger表达式触发器 继承于Trigger
// TriggerBuilder 用于构建触发器实例
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job1", "group1")
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
/**
* 将job2添加到容器中
*/
private void startJob2(Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(QuartzJob2.class).withIdentity("job2", "group2").build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0 0/1 * * * ?");
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job2", "group2")
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
/**
* 修改某个任务的执行时间
*/
public boolean modifyJob(String name, String group, String time) throws SchedulerException {
Date date = null;
TriggerKey triggerKey = new TriggerKey(name, group);
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
String oldTime = cronTrigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group)
.withSchedule(cronScheduleBuilder).build();
date = scheduler.rescheduleJob(triggerKey, trigger);
}
return date != null;
}
/**
* 获取job信息
*/
public String getJobInfo(String name, String group) throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(name, group);
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),
scheduler.getTriggerState(triggerKey).name());
}
/**
* 暂停所有任务
*/
public void pauseAllJob() throws SchedulerException {
scheduler.pauseAll();
}
/**
*暂停一个任务
*/
public void pauseJob(String name, String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null)
return;
scheduler.pauseJob(jobKey);
}
/**
* 恢复所有任务
*/
public void resumeAllJob() throws SchedulerException {
scheduler.resumeAll();
}
/**
* 恢复某个任务
*/
public void resumeJob(String name, String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null)
return;
scheduler.resumeJob(jobKey);
}
/**
*删除一个任务(从任务列表中删除)
*/
public void deleteJob(String name, String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null){
return;
}
scheduler.deleteJob(jobKey);
}
}
2.2.3编写ApplicationStartQuartzJobListener类,采用监听spring容器加载完毕后事件,启动任务调用;并将Scheduler交给spring初始化管理。
package com.xuexue.firstproject.Quartz;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
@Configuration
public class ApplicationStartQuartzJobListener implements ApplicationListener<ContextRefreshedEvent>{
@Autowired
private QuartzJobScheduler quartzJobScheduler;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
try {
//启动任务
quartzJobScheduler.startJob();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 初始注入scheduler
*/
@Bean
public Scheduler scheduler() throws SchedulerException{
SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
return schedulerFactoryBean.getScheduler();
}
}
2.2.4编译,启动,就会看到控制台输出定时任务打印的内容
至此,一个简单的Quartz定时任务框架demo就结束了。接下来要再此基础上丰富一下,我们上面的例子并没有进行数据库操作,但是再实际开发中,在任务中进行数据库操作时必不可免的。下面我们进行数据库的操作。
2.3Quartz框架中Job依赖注入service,实现数据库操作
2.3.1我们再写一个任务类job3(ps:不用过多关注我的LxJobInfoService 里面都写了什么了,你可以自己随意写一个从数据库中取数据的方法,然后控制台输出一下,方便验证。我的这个方法时获取job的list集合,后面会有体现)
package com.xuexue.firstproject.Quartz;
import com.xuexue.firstproject.Services.imp.LxJobInfoService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
public class QuartzJob3 implements Job {
@Autowired
private LxJobInfoService lxJobInfoService;
@Override
public void execute(JobExecutionContext jobExecutionContext) {
String cron= lxJobInfoService.getJobList().get(0).getJobCron();
System.out.println("job3 "+cron);
}
}
2.3.2将QuartzJobScheduler 类进行修改,添加startJob3()方法,并修改startJob()方法,修改后及新增后的方法如下;
/**
* 开始执行所有任务
*/
public void startJob() throws SchedulerException {
//startJob1(scheduler);
//startJob2(scheduler);
startJob3(scheduler);
scheduler.start();
}
/**
*将job3添加到容器中
*/
private void startJob3(Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(QuartzJob3.class).withIdentity("job3", "group3").build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job3", "group3")
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
2.3.3这样我们先试着执行一下,会发现,项目报 空指针异常。debug发现在任务中使用的service不能注入的,获取的对象为Null。
上网查找了一些资料,发现出现这个问题的原因:
在quartz框架中,Job 是通过反射出来的实例,不受spring的管理,所以就导致注入失败。Scheduler现在交给Spring生成,在Spirng-context-support jar包下org.springframework.scheduling.quartz包中有个SpringBeanJobFactory的类,job实例通过该类的createJobInstance方法创建。但是创建的job实例并没被spring管理,我们需要重写SpringBeanJobFactory类中的createJobInstance方法,将创建的job实例交给spring管理。接下来我们再写两个类来解决这个问题。
2.3.4定义MyJobFactory创建Quartz实例
package com.xuexue.firstproject.Quartz;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component
public class MyJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
2.3.5启动类注入MyJobFactory和Scheduler
package com.xuexue.firstproject;
import com.xuexue.firstproject.Quartz.MyJobFactory;
import lixue.ILiXue;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@SpringBootApplication
public class FirstprojectApplication {
@Autowired
private MyJobFactory myJobFactory;
public static void main(String[] args) {
SpringApplication.run(FirstprojectApplication.class, args);
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
// 延时启动
schedulerFactoryBean.setStartupDelay(20);
// 自定义Job Factory,用于Spring注入
schedulerFactoryBean.setJobFactory(myJobFactory);
System.out.println("myJobFactory:"+myJobFactory);
return schedulerFactoryBean;
}
@Bean
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
2.3.6删除ApplicationStartQuartzJobListener类中注入Scheduler 的代码
/**
* 初始注入scheduler
*/
@Bean
public Scheduler scheduler() throws SchedulerException{
SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
return schedulerFactoryBean.getScheduler();
}
2.3.7编译,启动,这次就成功的注入进去了,控制台输出结果
至此操作数据库的部分就结束了。Quartz定时任务的框架的优势就在于能够动态的管理我们的任务,那么接下来就写一个API管理我们的任务了
2.4Quartz的API
这个API只是一个简单的例子,在操作上的还是会有一些漏洞。仅供参考,主要是体验一下如何动态的操作任务的,更加丰富完善的功能,还有待大家丰富。如有错误,请大家批评指正。
2.4.1设计数据库,储存任务列表,创建对应的实体类
package com.xuexue.firstproject.Beans;
import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import java.io.Serializable;
import java.util.Date;
@TableName("lx_job_info")
public class LxJobInfo extends Model<LxJobInfo> {
/**
* 任务id
*/
@TableId(value = "job_id",type=IdType.AUTO)
private Integer jobId;
/**
* 任务的jobkey 唯一地识别一个Job
*/
private String jobName;
/**
* 任务的TriggerKey来唯一地识别一个Trigger
*/
private String jobGroup;
/**
* 任务的所在类的全路径
*/
private String jobClass;
/**
* 任务执行的cron表达式
*/
private String jobCron;
/**
* 任务状态 0停止状态,1启动状态,2暂停状态
*/
private Integer jobState;
/**
* 任务创建时间
*/
private Date jobCreatetime;
/**
* 任务修改时间
*/
private Date jobUpdatetime;
public Integer getJobId() {
return jobId;
}
public void setJobId(Integer jobId) {
this.jobId = jobId;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobGroup() {
return jobGroup;
}
public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}
public String getJobClass() {
return jobClass;
}
public void setJobClass(String jobClass) {
this.jobClass = jobClass;
}
public String getJobCron() {
return jobCron;
}
public void setJobCron(String jobCron) {
this.jobCron = jobCron;
}
public Integer getJobState() {
return jobState;
}
public void setJobState(Integer jobState) {
this.jobState = jobState;
}
public Date getJobCreatetime() {
return jobCreatetime;
}
public void setJobCreatetime(Date jobCreatetime) {
this.jobCreatetime = jobCreatetime;
}
public Date getJobUpdatetime() {
return jobUpdatetime;
}
public void setJobUpdatetime(Date jobUpdatetime) {
this.jobUpdatetime = jobUpdatetime;
}
@Override
protected Serializable pkVal() {
return null;
}
}
2.4.2对应的service
package com.xuexue.firstproject.Services;
import com.baomidou.mybatisplus.service.IService;
import com.xuexue.firstproject.Beans.LxJobInfo;
import java.util.List;
public interface ILxJobInfoService extends IService<LxJobInfo> {
//获取job的列表
List<LxJobInfo> getJobList();
//改变任务状态
void updateJobState(LxJobInfo lxJobInfo);
}
package com.xuexue.firstproject.Services.imp;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.xuexue.firstproject.Beans.LxJobInfo;
import com.xuexue.firstproject.Daos.LxJobInfoMapper;
import com.xuexue.firstproject.Services.ILxJobInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class LxJobInfoService extends ServiceImpl<LxJobInfoMapper,LxJobInfo> implements ILxJobInfoService{
@Autowired
LxJobInfoMapper lxJobInfoMapper;
/**
*查询任务列表
*/
@Override
public List<LxJobInfo> getJobList() {
return lxJobInfoMapper.getJobList();
//return lxJobInfoMapper.selectList(new EntityWrapper<>());
}
/**
*修改任务状态
*/
@Override
public void updateJobState(LxJobInfo lxJobInfo) {
lxJobInfoMapper.updateById(lxJobInfo);
}
}
2.4.3对应的mapper
package com.xuexue.firstproject.Daos;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.xuexue.firstproject.Beans.LxJobInfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface LxJobInfoMapper extends BaseMapper<LxJobInfo> {
//获取job的列表
List<LxJobInfo> getJobList();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xuexue.firstproject.Daos.LxJobInfoMapper">
<resultMap id="LxJobInfo" type="com.xuexue.firstproject.Beans.LxJobInfo" >
<result column="job_id" property="jobId" />
<result column="job_name" property="jobName" />
<result column="job_group" property="jobGroup" />
<result column="job_class" property="jobClass" />
<result column="job_cron" property="jobCron" />
<result column="job_state" property="jobState" />
<result column="job_createtime" property="jobCreatetime" />
<result column="job_updatetime" property="jobUpdatetime" />
</resultMap>
<sql id="Base_Column_List">
t.job_id,
t.job_name,
t.job_group,
t.job_class,
t.job_cron,
t.job_state,
t.job_createtime,
t.job_updatetime
</sql>
<select id="getJobList" resultMap="LxJobInfo">
select t.job_id,
t.job_name,
t.job_group,
t.job_class,
t.job_cron,
t.job_state,
t.job_createtime,
t.job_updatetime
from lx_job_info t
</select>
</mapper>
2.4.4对应的controller
package com.xuexue.firstproject.Controllers;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.xuexue.firstproject.Beans.LxJobInfo;
import com.xuexue.firstproject.Quartz.QuartzJobScheduler;
import com.xuexue.firstproject.Services.imp.LxJobInfoService;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@RequestMapping("/quartz")
public class QuartzApiController {
@Autowired
private QuartzJobScheduler quartzJobScheduler;
@Autowired
private LxJobInfoService lxJobInfoService;
/**
* @Author: lixue
* @Description: 访问quartz.html页面
* @Date: 2019/11/6 13:51
* @param
*/
@RequestMapping(value = "/toQuartz", method = RequestMethod.GET)
public String toQuartz(){
return "quartz/quartz.html";
}
/**
* @Author: lixue
* @Description: 查询任务列表
* @Date: 2019/11/5 9:45
* @param
*/
@RequestMapping(value = "/getJobList", method = RequestMethod.POST)
@ResponseBody
public List<LxJobInfo> getJobList(){
return lxJobInfoService.getJobList();
}
/**
* @Author: lixue
* @Description: 开启全部任务
* @Date: 2019/11/6 14:02
* @param
*/
@RequestMapping(value = "/startAll", method = RequestMethod.POST)
@ResponseBody
public String startAll(){
try {
//获取任务列表
List<LxJobInfo> list=lxJobInfoService.getJobList();
//调用开启所有任务的方法
quartzJobScheduler.startAllJob(list);
//改变数据库中任务的状态为开启状态
for(int i=0;i<list.size();i++){
list.get(i).setJobState(1);
lxJobInfoService.updateById(list.get(i));
}
//返回成功的字符串
return "{\"result\":true}";
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @Author: lixue
* @Description: 暂停全部任务
* @Date: 2019/11/6 17:15
* @param
*/
@RequestMapping(value = "/suspendAll", method = RequestMethod.POST)
@ResponseBody
public String suspendAll(){
try {
//调用暂停所有任务的方法
quartzJobScheduler.pauseAllJob();
//改变数据库中任务的状态为暂停状态
List<LxJobInfo> list=lxJobInfoService.getJobList();
for(int i=0;i<list.size();i++){
list.get(i).setJobState(2);
lxJobInfoService.updateById(list.get(i));
//lxJobInfoService.updateJobState(list.get(i));
}
//返回成功的字符串
return "{\"result\":true}";
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @Author: lixue
* @Description: 开启一个任务
* @Date: 2019/11/6 17:14
* @param jobId
*/
@RequestMapping(value = "/startOne", method = RequestMethod.POST)
@ResponseBody
public String startOne(Integer jobId){
try {
LxJobInfo lxJobInfo=lxJobInfoService.selectById(jobId);
//调用开启所有任务的方法
quartzJobScheduler.startOneJob(lxJobInfo);
//改变数据库中任务的状态为开启状态
lxJobInfo.setJobState(1);
lxJobInfoService.updateById(lxJobInfo);
//返回成功的字符串
return "{\"result\":true}";
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @Author: lixue
* @Description: 暂停一个任务
* @Date: 2019/11/6 17:14
* @param jobId
*/
@RequestMapping(value = "/suspendOne", method = RequestMethod.POST)
@ResponseBody
public String suspendOne(Integer jobId){
try {
LxJobInfo lxJobInfo=lxJobInfoService.selectById(jobId);
//调用开启所有任务的方法
quartzJobScheduler.pauseJob(lxJobInfo.getJobName(),lxJobInfo.getJobGroup());
//改变数据库中任务的状态为暂停状态
lxJobInfo.setJobState(2);
lxJobInfoService.updateById(lxJobInfo);
//返回成功的字符串
return "{\"result\":true}";
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @Author: lixue
* @Description: 恢复一个任务
* @Date: 2019/11/7 8:56
* @param jobId
*/
@RequestMapping(value = "/resumeOne", method = RequestMethod.POST)
@ResponseBody
public String resumeOne(Integer jobId){
try {
LxJobInfo lxJobInfo=lxJobInfoService.selectById(jobId);
//调用开启所有任务的方法
quartzJobScheduler.resumeJob(lxJobInfo.getJobName(),lxJobInfo.getJobGroup());
//改变数据库中任务的状态为启动状态
lxJobInfo.setJobState(1);
lxJobInfoService.updateById(lxJobInfo);
//返回成功的字符串
return "{\"result\":true}";
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@RequestMapping(value = "/addOneJob", method = RequestMethod.POST)
@ResponseBody
public String addOneJob(LxJobInfo lxJobInfo){
lxJobInfoService.insert(lxJobInfo);
return "{\"result\":true}";
}
}
2.4.5对应的页面和js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="tablediv">
<button id="startAll">开启全部任务</button>
<button id="suspendAll">暂停全部任务</button>
<table id="quartzTable"></table>
</div>
<from id="addJobFrom">
name:<input type="text" id="jobName" name="jobName">
group:<input type="text" id="jobGroup" name="jobName">
类全路径:<input type="text" id="jobClass" name="jobName">
cron:<input type="text" id="jobCron" name="jobName">
<button id="addOneJob">添加任务</button>
</from>
<script type="text/javascript" src="../static/jquery.min.js"></script>
<script type="text/javascript" src="../static/quartz/quartz.js"></script>
</body>
</html>
$(function(){
getJobList();
/**
* 点击开启全部
*/
$("#startAll").click(function(){
//发送ajax请求
$.ajax({
url:"startAll",
type:"post",
dataType:"json",
success:function(data){
if (data){
alert("开启成功");
}else{
alert("开启失败");
}
//刷新表格
//$(".state").html("启动状态");
getJobList();
},
error:function(err){
console.log(err);
}
})
});
/**
* 点击暂停全部
*/
$("#suspendAll").click(function(){
//发送ajax请求
$.ajax({
url:"suspendAll",
type:"post",
dataType:"json",
success:function(data){
if (data){
alert("暂停成功");
}else{
alert("暂停失败");
}
//刷新表格
//$(".state").html("暂停状态");
getJobList();
},
error:function(err){
console.log(err);
}
});
});
/**
* 点击添加任务
*/
})
/**
* 展示任务列表
*/
function getJobList(){
$("#quartzTable").html("");
$.ajax({
url:"getJobList",
type:"post",
dataType:"json",
success:function(data){
//console.log(data);
var str='<tr><th>序号</th><th>name</th><th>group</th><th>类全路径</th><th>cron</th><th>状态</th><th>操作</th></tr>';
for (var i=0;i<data.length;i++){
str+='<tr>';
str+='<td>'+data[i].jobId+'</td>';
str+='<td>'+data[i].jobName+'</td>';
str+='<td>'+data[i].jobGroup+'</td>';
str+='<td>'+data[i].jobClass+'</td>';
str+='<td>'+data[i].jobCron+'</td>';
var state="";
if(data[i].jobState==0){
state="停止状态";
}
if(data[i].jobState==1){
state="启动状态";
}
if(data[i].jobState==2){
state="暂停状态";
}
str+='<td><button class="state">'+state+'</button></td>';
str+='<td><button onclick="startOneJob('+data[i].jobId+')">开启</button>' +
'<button onclick="resumeOneJob('+data[i].jobId+')">恢复</button>' +
'<button onclick="suspendOneJob('+data[i].jobId+')">暂停</button>' +
'<button onclick="stopOneJob('+data[i].jobId+')">停止</button></td>';
str+='</tr>';
}
$("#quartzTable").html(str);
},
error:function(err){
console.log(err);
}
});
}
/**
* 开启一个任务
*/
function startOneJob(jobId){
$.ajax({
url:"startOne",
type:"post",
data:{jobId:jobId},
dataType:"json",
success:function(data){
if (data){
alert("启动成功");
}else{
alert("启动失败");
}
//刷新表格
//$(".state").html("暂停状态");
getJobList();
},
error:function(err){
console.log(err);
}
});
}
/**
* 暂停一个任务
*/
function suspendOneJob(jobId){
$.ajax({
url:"suspendOne",
type:"post",
data:{jobId:jobId},
dataType:"json",
success:function(data){
if (data){
alert("暂停成功");
}else{
alert("暂停失败");
}
//刷新表格
//$(".state").html("暂停状态");
getJobList();
},
error:function(err){
console.log(err);
}
});
}
/**
* 恢复一个任务
*/
function resumeOneJob(jobId){
$.ajax({
url:"resumeOne",
type:"post",
data:{jobId:jobId},
dataType:"json",
success:function(data){
if (data){
alert("恢复成功");
}else{
alert("恢复失败");
}
//刷新表格
//$(".state").html("暂停状态");
getJobList();
},
error:function(err){
console.log(err);
}
});
}
2.4.6修改一下我们前面的QuartzJobScheduler类
package com.xuexue.firstproject.Quartz;
import com.xuexue.firstproject.Beans.LxJobInfo;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.Date;
import java.util.List;
@Configuration
public class QuartzJobScheduler {
// 任务调度
@Autowired
private Scheduler scheduler;
/**
* 开始执行所有任务
*/
public void startJob() throws SchedulerException {
//startJob1(scheduler);
//startOneJob(scheduler,"job2","group2","0 0/1 * * * ?",QuartzJob2.class);
//startJob2(scheduler);
startJob3(scheduler);
scheduler.start();
}
/**
* 修改某个任务的执行时间
*
* @param name
* @param group
* @param time
* @return
* @throws SchedulerException
*/
public boolean modifyJob(String name, String group, String time) throws SchedulerException {
Date date = null;
TriggerKey triggerKey = new TriggerKey(name, group);
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
String oldTime = cronTrigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group)
.withSchedule(cronScheduleBuilder).build();
date = scheduler.rescheduleJob(triggerKey, trigger);
}
return date != null;
}
/**
* @Author: lixue
* @Description: 获取job信息
* @Date: 2019/11/4 14:38
* @param name
* @param group
*/
public String getJobInfo(String name, String group) throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(name, group);
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),
scheduler.getTriggerState(triggerKey).name());
}
/**
* @Author: lixue
* @Description: 暂停所有任务
* @Date: 2019/11/4 14:18
* @param
*/
public void pauseAllJob() throws SchedulerException {
scheduler.pauseAll();
}
/**
* @Author: lixue
* @Description: 暂停一个任务
* @Date: 2019/11/4 14:17
* @param name
* @param group
*/
public void pauseJob(String name, String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null)
return;
scheduler.pauseJob(jobKey);
}
/**
* 恢复所有任务
*
* @throws SchedulerException
*/
public void resumeAllJob() throws SchedulerException {
scheduler.resumeAll();
}
/**
* 恢复某个任务
*
* @param name
* @param group
* @throws SchedulerException
*/
public void resumeJob(String name, String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null)
return;
scheduler.resumeJob(jobKey);
}
/**
* @Author: lixue
* @Description: 删除一个任务(从任务列表中删除)
* @Date: 2019/10/30 17:29
* @param name
* @param group
*/
public void deleteJob(String name, String group) throws SchedulerException {
JobKey jobKey = new JobKey(name, group);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null){
return;
}
scheduler.deleteJob(jobKey);
}
/**
* @Author: lixue
* @Description: 开启全部job(添加进任务列表,并启动)
* @Date: 2019/10/28 10:38
* @param lxJobInfoList job的list集合
*/
public void startAllJob(List<LxJobInfo> lxJobInfoList)throws SchedulerException,ClassNotFoundException{
for(int i=0;i<lxJobInfoList.size();i++){
//反射机制
Class jobClass = Class.forName(lxJobInfoList.get(i).getJobClass());
// 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例
// JobDetail 是具体Job实例
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(lxJobInfoList.get(i).getJobName(), lxJobInfoList.get(i).getJobGroup()).build();
// 基于表达式构建触发器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(lxJobInfoList.get(i).getJobCron());
// CronTrigger表达式触发器 继承于Trigger
// TriggerBuilder 用于构建触发器实例
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(lxJobInfoList.get(i).getJobName(), lxJobInfoList.get(i).getJobGroup())
.withSchedule(cronScheduleBuilder).build();
//让调度器 使触发器和任务关联
scheduler.scheduleJob(jobDetail, cronTrigger);
}
//启动任务调度器
scheduler.start();
}
/**
* @Author: lixue
* @Description: 开启一个job(添加进任务列表,并启动)
* @Date: 2019/10/28 15:44
* @param lxJobInfo 一个job对象
*/
public void startOneJob(LxJobInfo lxJobInfo) throws SchedulerException ,ClassNotFoundException{
Class jobClass = Class.forName(lxJobInfo.getJobClass());
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(lxJobInfo.getJobName(), lxJobInfo.getJobGroup()).build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(lxJobInfo.getJobCron());
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(lxJobInfo.getJobName(), lxJobInfo.getJobGroup())
.withSchedule(cronScheduleBuilder).build();
//让调度器 使触发器和任务关联
scheduler.scheduleJob(jobDetail, cronTrigger);
//启动任务调度器
scheduler.start();
}
/*
/**
* @Author: lixue
* @Description: 开启一个job 暂时不适用
* @Date: 2019/10/26 14:15
* @param scheduler 任务调度
* @param name jobkey 唯一地识别一个Job
* @param group TriggerKey来唯一地识别一个Trigger
* @param cron corn表达式
* @param classs 任务的类
*/
/*public void startOneJob(Scheduler scheduler, String name, String group, String cron, Class classs) throws SchedulerException {
//Class.forName("com.");
JobDetail jobDetail = JobBuilder.newJob(classs).withIdentity(name, group).build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(name, group)
.withSchedule(cronScheduleBuilder).build();
//让调度器 使触发器和任务关联
scheduler.scheduleJob(jobDetail, cronTrigger);
//启动任务调度器
//scheduler.start();
}*/
private void startJob1(Scheduler scheduler) throws SchedulerException {
// 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例
// JobDetail 是具体Job实例
JobDetail jobDetail = JobBuilder.newJob(QuartzJob1.class).withIdentity("job1", "group1").build();
// 基于表达式构建触发器
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
// CronTrigger表达式触发器 继承于Trigger
// TriggerBuilder 用于构建触发器实例
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job1", "group1")
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
private void startJob2(Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(QuartzJob2.class).withIdentity("job2", "group2").build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0 0/1 * * * ?");
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job2", "group2")
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
private void startJob3(Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(QuartzJob3.class).withIdentity("job3", "group3").build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job3", "group3")
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}
2.4.7因为采用的是自动控制的,所以我们要将ApplicationStartQuartzJobListener类里面的内容注释掉,并让这个类不再实现ApplicationListener<ContextRefreshedEvent>
2.4.8编译,运行后出现如下页面
点击开启会开启一个任务,点击开启全部会开启全部任务,点击暂停会暂停任务,点击恢复会回复任务。
2.4.9项目中的瑕疵
这个小练习只是体验一下quartz框架的使用,在写的程序会有比较多的逻辑漏洞,还请大家谅解,更严谨完善的功能还需要大家自己根据实际情况进行完善以及修改。
1、开启全部任务没有筛选出已经开启的任务
2、关闭程序后再开启,状态显示的与实际情况不符
3、添加功能是没有实现的
三、感谢
在学习过程中,参考了很多的文章,如下几篇文章对我的帮助很大。
https://blog.csdn.net/u013042707/article/details/82934725