目錄
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