初學SpringBoot之集成Quartz定時框架

目錄

一、前言

二、開啓Quartz之旅

2.1 Quartz框架介紹

2.2 從一個簡單的demo開始

2.3Quartz框架中Job依賴注入service,實現數據庫操作

2.4Quartz的API

三、感謝


一、前言

    這篇是學習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

https://blog.csdn.net/upxiaofeng/article/details/79415108

https://blog.csdn.net/fly_captain/article/details/84257781

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章