SpringBoot2.0整合quartz實現多定時任務動態配置,實現任務增刪改,生成Cron表達式

部分內容轉載自“爾笑惹千愁”,鏈接https://blog.csdn.net/lx1309244704/article/details/81810373
在我們日常的開發中,很多時候,定時任務都不是寫死的,而是寫到數據庫中,從而實現定時任務的動態配置,下面就通過一個簡單的示例,來實現這個功能。

一、添加依賴包

<!-- quartz -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-quartz</artifactId>
		</dependency>

二、創建調度器

package com.quartz;

import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
public class SchedulerConfig implements SchedulerFactoryBeanCustomizer{
	
	@Override
	public void customize(SchedulerFactoryBean schedulerFactoryBean) {
		 	schedulerFactoryBean.setStartupDelay(2);
	        schedulerFactoryBean.setAutoStartup(true);
	        schedulerFactoryBean.setOverwriteExistingJobs(true);
	}

}

三、application.yml配置
這裏需要注意的幾點:
1、如果你的框架是已經搭建好,只需要在yml中添加quartz相關屬性配置和數據庫方式配置
2、quartz相關屬性配置是在spring下管理,注意縮進格式,如果位置或者縮進不對程序啓動後也不會報錯

server:
    port: 8003
 
# 默認的profile爲dev,其他環境通過指定啓動參數使用不同的profile,比如:  
#   測試環境:java -jar quartz-service.jar --spring.profiles.active=test  
#   生產環境:java -jar quartz-service.jar --spring.profiles.active=prod  
spring: 
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource   #這裏是配置druid連接池,以下都是druid的配置信息
    url: jdbc:mysql://0.0.0.0:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: ******
  
  quartz:
    #相關屬性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: clusteredScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: true
            clusterCheckinInterval: 10000
            useProperties: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
    #數據庫方式
    job-store-type: jdbc
      
mybatis-plus:
  mapper-locations: classpath*:/mapper/**Mapper.xml    #把xml文件放在com.XX.mapper.*中可能會出現找到的問題,這裏把他放在resource下的mapper中
  typeAliasesPackage: com.quartz.domain         #這裏是實體類的位置,#實體掃描,多個package用逗號或者分號分隔
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
 
logging:
  file: quartz-service.log
  level:
    com.quartz: debug  

四、JobController

package com.south.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.south.data.vo.JobAndTriggerDto;
import com.south.service.IJobAndTriggerService;
import com.south.utils.PaginationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.List;

/**
 * @Classname JobController
 * @Description TODO
 * @Date 2019/7/31 14:11
 * @Created by zhangzhenjun
 */
@Slf4j
@RestController
@RequestMapping("/api")
public class JobResource {

    @Autowired
    private IJobAndTriggerService jobAndTriggerService;

    public JobResource(IJobAndTriggerService jobAndTriggerService){
        this.jobAndTriggerService = jobAndTriggerService;
    }

    @PostMapping(value = "/datagrid")
    public ResponseEntity<List<JobAndTriggerDto>> queryjob(Pageable pageable, @RequestParam MultiValueMap<String, String> queryParams, UriComponentsBuilder uriBuilder) {
        log.debug("queryjob");
        IPage<JobAndTriggerDto> page = jobAndTriggerService.getPageJob(pageable, queryParams);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(uriBuilder.queryParams(queryParams), page);
        return ResponseEntity.ok().headers(headers).body(page.getRecords());
    }

    /**
     * @Title: addJob
     * @Description: TODO(添加Job)
     * @param jobClassName
     *            類名
     * @param jobGroupName
     *            組名
     * @param cronExpression
     *            表達式,如:0/5 * * * * ? (每隔5秒)
     */
    @PostMapping(value = "/add")
    public ResponseEntity addJob(
            @RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName,
            @RequestParam(value = "cronExpression") String cronExpression){
        try {
            jobAndTriggerService.addJob(jobClassName, jobGroupName, cronExpression);
            return ResponseEntity.ok().body("操作成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.ok().body("操作失敗");
        }
    }

    /**
     * @Title: pauseJob
     * @Description: TODO(暫停Job)
     * @param jobClassName
     *            類名
     * @param jobGroupName
     *            組名
     */
    @PostMapping(value = "/pause")
    public ResponseEntity pauseJob(
            @RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName) {
        try {
            jobAndTriggerService.pauseJob(jobClassName, jobGroupName);
            return ResponseEntity.ok().body("操作成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.ok().body("操作失敗");
        }
    }

    /**
     * @Title: resumeJob
     * @Description: TODO(恢復Job)
     * @param jobClassName
     *            類名
     * @param jobGroupName
     *            組名
     */
    @PostMapping(value = "/resume")
    public ResponseEntity resumeJob(
            @RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName) {
        try {
            jobAndTriggerService.resumejob(jobClassName, jobGroupName);
            return ResponseEntity.ok().body("操作成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.ok().body("操作失敗");
        }
    }

    /**
     * @Title: rescheduleJob
     * @Description: TODO(重新設置Job)
     * @param jobClassName
     *            類名
     * @param jobGroupName
     *            組名
     * @param cronExpression
     *            表達式
     */
    @PostMapping(value = "/reschedule")
    public ResponseEntity rescheduleJob(
            @RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName,
            @RequestParam(value = "cronExpression") String cronExpression) {
        try {
            jobAndTriggerService.updateJob(jobClassName, jobGroupName, cronExpression);
            return ResponseEntity.ok().body("操作成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.ok().body("操作失敗");
        }
    }

    /**
     * @Title: deleteJob
     * @Description: TODO(刪除Job)
     * @param jobClassName
     *            類名
     * @param jobGroupName
     *            組名
     */
    @RequestMapping(value = "/del", method = RequestMethod.POST)
    public ResponseEntity deleteJob(@RequestParam(value = "jobClassName") String jobClassName, @RequestParam(value = "jobGroupName") String jobGroupName) {
        try {
            jobAndTriggerService.deleteJob(jobClassName, jobGroupName);
            return ResponseEntity.ok().body("操作成功");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.ok().body("操作失敗");
        }
    }

}

五、IJobAndTriggerService

package com.south.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.south.data.vo.JobAndTriggerDto;
import org.springframework.data.domain.Pageable;
import org.springframework.util.MultiValueMap;

import java.util.Map;

/**
 * @Classname IJobAndTriggerService
 * @Description TODO
 * @Date 2019/7/31 14:15
 * @Created by zhangzhenjun
 */
public interface IJobAndTriggerService extends IService<JobAndTriggerDto> {

    /**
     * @Title: getPageJob
     * @Description: TODO(查詢定時任務,分頁)
     * @param @param search
     * @param @return    參數
     * @return Map<String,Object>    返回類型
     * @throws
     */
    IPage<JobAndTriggerDto> getPageJob(Pageable pageable, MultiValueMap queryParam);

    /**
     * @Title: getPageJobmod
     * @Description: TODO(查詢定時任務)
     * @param @return    參數
     * @return JobAndTriggerDto    返回類型
     * @throws
     */
    JobAndTriggerDto getPageJobmod();

    /**
     * @Title: addJob
     * @Description: TODO(添加任務)
     * @param @param jobClassName 任務路徑名稱
     * @param @param jobGroupName 任務分組
     * @param @param cronExpression cron時間規則
     * @param @throws Exception    參數
     * @return void    返回類型
     * @throws
     */
    void addJob(String jobClassName, String jobGroupName, String cronExpression) throws Exception;

    /**
     * @Title: addJob
     * @Description: TODO(添加動態任務)
     * @param @param jobClassName 任務路徑名稱
     * @param @param jobGroupName 任務分組
     * @param @param cronExpression cron時間規則
     * @param @param jobDescription 參數
     * @param @param params
     * @param @throws Exception  參數說明
     * @return void    返回類型
     * @throws
     */
    void addJob(String jobClassName, String jobGroupName, String cronExpression, String jobDescription, Map<String, Object> params) throws Exception;

    /**
     * @Title: updateJob
     * @Description: TODO(更新定時任務)
     * @param @param jobClassName 任務路徑名稱
     * @param @param jobGroupName 任務分組
     * @param @param cronExpression cron時間規則
     * @param @throws Exception    參數
     * @return void    返回類型
     * @throws
     */
    void updateJob(String jobClassName, String jobGroupName, String cronExpression) throws Exception;

    /**
     * @Title: deleteJob
     * @Description: TODO(刪除定時任務)
     * @param @param jobClassName 任務路徑名稱
     * @param @param jobGroupName 任務分組
     * @param @throws Exception    參數
     * @return void    返回類型
     * @throws
     */
    void deleteJob(String jobClassName, String jobGroupName) throws Exception;

    /**
     * @Title: pauseJob
     * @Description: TODO(暫停定時任務)
     * @param @param jobClassName 任務路徑名稱
     * @param @param jobGroupName 任務分組
     * @param @throws Exception    參數
     * @return void    返回類型
     * @throws
     */
    void pauseJob(String jobClassName, String jobGroupName) throws Exception;

    /**
     * @Title: resumejob
     * @Description: TODO(恢復任務)
     * @param @param jobClassName 任務路徑名稱
     * @param @param jobGroupName 任務分組
     * @param @throws Exception    參數
     * @return void    返回類型
     * @throws
     */
    void resumejob(String jobClassName, String jobGroupName) throws Exception;

}

六、IJobAndTriggerServiceImpl

package com.south.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.south.data.mapper.JobAndTriggerMapper;
import com.south.data.vo.JobAndTriggerDto;
import com.south.job.BaseJob;
import com.south.service.IJobAndTriggerService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.CronTrigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;

import java.util.Iterator;
import java.util.Map;

/**
 * @Classname IJobAndTriggerServiceImpl
 * @Description TODO
 * @Date 2019/7/31 14:30
 * @Created by zhangzhenjun
 */
@Slf4j
@Service
@Transactional
public class IJobAndTriggerServiceImpl extends ServiceImpl<JobAndTriggerMapper, JobAndTriggerDto> implements IJobAndTriggerService {

    @Autowired
    private Scheduler scheduler;

    @Override
    public IPage<JobAndTriggerDto> getPageJob(Pageable pageable, MultiValueMap queryParam) {
        IPage<JobAndTriggerDto> page = new Page<>(pageable.getPageNumber(), pageable.getPageSize());
        return baseMapper.getJobAndTriggerDetails(page);
    }

    @Override
    public JobAndTriggerDto getPageJobmod() {
        return baseMapper.getJobAndTriggerDto();
    }

    @Override
    public void addJob(String jobClassName, String jobGroupName, String cronExpression) throws Exception {
        // 啓動調度器
        scheduler.start();

        // 構建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass())
                .withIdentity(jobClassName, jobGroupName).build();

        // 表達式調度構建器(即任務執行的時間)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

        // 按新的cronExpression表達式構建一個新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName)
                .withSchedule(scheduleBuilder).build();

        try {
            scheduler.scheduleJob(jobDetail, trigger);
            System.out.println("創建定時任務成功");

        } catch (SchedulerException e) {
            System.out.println("創建定時任務失敗" + e);
            throw new Exception("創建定時任務失敗");
        }

    }

    @Override
    public void addJob(String jobClassName, String jobGroupName, String cronExpression, String jobDescription,
                       Map<String, Object> params) throws Exception {

        // 啓動調度器
        scheduler.start();

        // 構建job信息
        JobDetail jobDetail = JobBuilder.newJob(IJobAndTriggerServiceImpl.getClass(jobClassName).getClass())
                .withIdentity(jobClassName, jobGroupName).withDescription(jobDescription).build();
        Iterator<Map.Entry<String, Object>> var7 = params.entrySet().iterator();
        while(var7.hasNext()) {
            Map.Entry<String, Object> entry = var7.next();
            jobDetail.getJobDataMap().put((String)entry.getKey(), entry.getValue());
        }
        System.out.println("jobDetail數據:--------"+jobDetail.toString());
        // 表達式調度構建器(即任務執行的時間)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

        // 按新的cronExpression表達式構建一個新的trigger
        CronTrigger trigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName)
                .withSchedule(scheduleBuilder).build();

        try {
            scheduler.scheduleJob(jobDetail, trigger);
            System.out.println("創建定時任務成功");

        } catch (SchedulerException e) {
            System.out.println("創建定時任務失敗" + e);
            throw new Exception("創建定時任務失敗");
        }
    }

    @Override
    public void updateJob(String jobClassName, String jobGroupName, String cronExpression) throws Exception {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);
            // 表達式調度構建器(動態修改後不立即執行)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            // 按新的cronExpression表達式重新構建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            // 按新的trigger重新設置job執行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            System.out.println("更新定時任務失敗" + e);
            throw new Exception("更新定時任務失敗");
        }
    }

    @Override
    public void deleteJob(String jobClassName, String jobGroupName) throws Exception {
        scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));
        scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));
        scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));
    }

    @Override
    public void pauseJob(String jobClassName, String jobGroupName) throws Exception {
        scheduler.pauseJob(JobKey.jobKey(jobClassName, jobGroupName));
    }

    @Override
    public void resumejob(String jobClassName, String jobGroupName) throws Exception {
        scheduler.resumeJob(JobKey.jobKey(jobClassName, jobGroupName));
    }

    public static BaseJob getClass(String classname) throws Exception {
        Class<?> class1 = Class.forName(classname);
        return (BaseJob) class1.newInstance();
    }

}

七、JobAndTriggerMapper

package com.south.data.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.south.data.vo.JobAndTriggerDto;

/**
 * @Classname JobAndTriggerMapper
 * @Description TODO
 * @Date 2019/7/31 14:31
 * @Created by zhangzhenjun
 */
public interface JobAndTriggerMapper extends BaseMapper<JobAndTriggerDto> {

    IPage<JobAndTriggerDto> getJobAndTriggerDetails(IPage<JobAndTriggerDto> page);

    JobAndTriggerDto getJobAndTriggerDto();

}

八、JobAndTriggerMapper.xml

<?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.south.data.mapper.JobAndTriggerMapper">

    <select id="getJobAndTriggerDetails" resultType="com.south.data.vo.JobAndTriggerDto">
			SELECT
				jd.JOB_NAME AS jobName,
				jd.DESCRIPTION AS jobDescription,
				jd.JOB_GROUP AS jobGroupName,
				jd.JOB_CLASS_NAME AS jobClassName,
				t.TRIGGER_NAME AS triggerName,
				t.TRIGGER_GROUP AS triggerGroupName,
				FROM_UNIXTIME(t.PREV_FIRE_TIME/1000,'%Y-%m-%d %T') AS prevFireTime,
				FROM_UNIXTIME(t.NEXT_FIRE_TIME/1000,'%Y-%m-%d %T') AS nextFireTime,
				ct.CRON_EXPRESSION AS cronExpression,
				t.TRIGGER_STATE AS triggerState
			FROM
				qrtz_job_details jd
			JOIN qrtz_triggers t
			JOIN qrtz_cron_triggers ct ON jd.JOB_NAME = t.JOB_NAME
			AND t.TRIGGER_NAME = ct.TRIGGER_NAME
			AND t.TRIGGER_GROUP = ct.TRIGGER_GROUP
    </select>

    <select id="getJobAndTriggerDto" resultType="com.south.data.vo.JobAndTriggerDto">
			SELECT
				jd.JOB_NAME AS jobName,
				jd.DESCRIPTION AS jobDescription,
				jd.JOB_GROUP AS jobGroupName,
				jd.JOB_CLASS_NAME AS jobClassName,
				t.TRIGGER_NAME AS triggerName,
				t.TRIGGER_GROUP AS triggerGroupName,
				FROM_UNIXTIME(t.PREV_FIRE_TIME/1000,'%Y-%m-%d %T') AS prevFireTime,
				FROM_UNIXTIME(t.NEXT_FIRE_TIME/1000,'%Y-%m-%d %T') AS nextFireTime,
				ct.CRON_EXPRESSION AS cronExpression,
				t.TRIGGER_STATE AS triggerState
			FROM
				qrtz_job_details jd
			JOIN qrtz_triggers t
			JOIN qrtz_cron_triggers ct ON jd.JOB_NAME = t.JOB_NAME
			AND t.TRIGGER_NAME = ct.TRIGGER_NAME
			AND t.TRIGGER_GROUP = ct.TRIGGER_GROUP
    </select>

</mapper>

九、BaseJob

package com.south.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * @Classname BaseJob
 * @Description TODO
 * @Date 2019/7/31 14:52
 * @Created by zhangzhenjun
 */
public interface BaseJob extends Job {

   public void execute(JobExecutionContext context) throws JobExecutionException;

}

十、HelloJob實例,在這裏面寫定時任務要執行的內容

package com.south.job;

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * @Classname HelloJob
 * @Description TODO
 * @Date 2019/7/31 14:10
 * @Created by zhangzhenjun
 */
@Slf4j
public class HelloJob implements BaseJob {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        log.error("Hello Job執行時間: " + new Date());
        System.err.println("Hello Job執行時間: " + new Date());
    }  
}  

十一、啓動類加上@EnableScheduling註解,在項目啓動時加載定時任務,啓動Job
在這裏插入圖片描述
項目效果:
在這裏插入圖片描述
在這裏插入圖片描述
如果需要源碼,請跳轉自本博文最上方的轉載鏈接;

最後再附上Java轉Cron表達式的工具類,具體代碼在我的另一篇博文:java生成cron表達式
鏈接:https://blog.csdn.net/qq_42567801/article/details/98172088

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