SpringBoot整合Quartz定时任务持久化到数据库的开发。超详细,可用

开发环境准备

开发环境:

  1. jdk11
  2. springboot 2.4.3
  3. quartz 2.3.2
  4. mysql
  5. mybatis-plus

1. gradle导入依赖

	implementation 'org.springframework.boot:spring-boot-starter-web'
	compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.3.2'
    compile group: 'com.baomidou', name: 'mybatis-plus-generator', version: '3.3.2'
    implementation group: 'org.springframework', name: 'spring-context-support', version: '5.2.12.RELEASE'
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
    implementation group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.2'
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs
    implementation group: 'org.quartz-scheduler', name: 'quartz-jobs', version: '2.3.0'
        testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

maven方式导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.4version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.2.4version>
        dependency>
        
        <dependency>
            <groupId>org.quartz-schedulergroupId>
            <artifactId>quartzartifactId>
            <version>${quartz.version}version>
        dependency>
        <dependency>
            <groupId>org.quartz-schedulergroupId>
            <artifactId>quartz-jobsartifactId>
            <version>${quartz.version}version>
        dependency>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-context-supportartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

2、创建mysql数据库

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);


commit;

编写quartz.properties配置类

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#


#设置默认调度程序的名称,如正在使用群集功能,则必须对群集中“逻辑上”相同的调度程序的每个实例使用相同的名称,重新赋值该值。
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则为true。
org.quartz.scheduler.rmi.export = false
#如果要连接(使用)远程服务的调度程序,则为true。还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
org.quartz.scheduler.rmi.proxy = false
#org.quartz.scheduler.rmi.registryHost
#org.quartz.scheduler.rmi.registryPort
#设置这项为true使我们在调用job的execute()之前能够开始一个UserTransaction。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

#指定的线程池
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#线程数量
org.quartz.threadPool.threadCount = 10
#优先级
org.quartz.threadPool.threadPriority = 5
#自创建父线程
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

#作业最大延迟时间毫秒
org.quartz.jobStore.misfireThreshold = 60000
#数据保存方式为持久化
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

application.yml配置

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/Test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update #ddl-auto:设为update表示每次都不会重新建表
    show-sql: true
  application:
    name: quartz_demo
server:
  port: 8081
# 打印日志
logging:
  level:
    root: INFO
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
    com.springms: DEBUG
mybatis:
  mapper-locations: classpath:mapper/*.xml

创建接口

DynamicJob

package com.example.df.quartz.job;

import com.example.df.util.QuartzreflectUtil;
import com.example.df.util.SpringContextUtil;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;

/**
 * @Author: xu
 * @Description: TODO	定时任务需要实现的接口
 * @Date: 2021/11/29 15:39
 */
public interface DynamicJob<T> extends Job
{
	/**
	 * 需要执行任务的方法体
	 * @param args	可变参数,前端传递
	 * @return	object
	 */
	T task(Object... args);

	@Override
	default void execute(JobExecutionContext context) {
		QuartzreflectUtil quartzreflectUtil = SpringContextUtil.getBean(QuartzreflectUtil.class);
		quartzreflectUtil.reflect(context);
	}
}

接受参数的实体对象Params

package com.example.df.quartz.entry;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Params {

	@NotNull(message = "任务名必填!")
	private String jobName;

	@NotNull(message = "分组名必填!")
	private String jobGroupName;

	@NotNull(message = "类名不能为空")
	private String className;

	@NotNull(message = "执行cron必填!")
	private String cron;

	private String method;

	private List<Map<String,Object>> paramData;

	private Object[] finalMethodParam;
}

Quartz配置类,对象工厂注入spring

package com.example.df.conf;

import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

import javax.sql.DataSource;

/**
 * @author xu
 * @Description
 * @createTime 2021年03月08日 17:31:00
 */
@Configuration
@EnableScheduling
public class QuartzConfiguration {
    /**
     * 继承org.springframework.scheduling.quartz.SpringBeanJobFactory
     * 实现任务实例化方式
     */
    public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
            ApplicationContextAware {

        private transient AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        /**
         * 将job实例交给spring ioc托管
         * 我们在job实例实现类内可以直接使用spring注入的调用被spring ioc管理的实例
         *
         * @param bundle
         * @return
         * @throws Exception
         */
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            /**
             * 将job实例交付给spring ioc
             */
            beanFactory.autowireBean(job);
            return job;
        }
    }

    /**
     * 配置任务工厂实例
     *
     * @return
     */
    @Bean
    public JobFactory jobFactory() {
        /**
         * 采用自定义任务工厂 整合spring实例来完成构建任务*/
        return new AutowiringSpringBeanJobFactory();
    }

    /**
     * 配置任务调度器
     * 使用项目数据源作为quartz数据源
     *
     * @param jobFactory 自定义配置任务工厂
     * @param dataSource 数据源实例
     * @return
     * @throws Exception
     */
    @Bean(destroyMethod = "destroy", autowire = Autowire.NO)
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) throws Exception {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        //将spring管理job自定义工厂交由调度器维护
        schedulerFactoryBean.setJobFactory(jobFactory);
        //设置覆盖已存在的任务
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //项目启动完成后,等待2秒后开始执行调度器初始化
        schedulerFactoryBean.setStartupDelay(2);
        //设置调度器自动运行
        schedulerFactoryBean.setAutoStartup(true);
        //设置数据源,使用与项目统一数据源
        schedulerFactoryBean.setDataSource(dataSource);
        //设置上下文spring bean name
        schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
        //设置配置文件位置
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
        return schedulerFactoryBean;
    }
}



以上操作环境准备完成

QuartzUtil所使用到的工具类方法

  1. 其中包含传入类名和方法名,通过反射得到该类中具体方法,然后调用
  2. 从list对象取出数据存入obj[]对象数组中
  3. 将map数据依次取出,存入list中
package com.example.df.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.df.quartz.job.DynamicJob;
import com.example.df.quartz.service.QuartzService;
import com.example.df.quartz.entry.Params;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author xu
 * @Description
 * @createTime 2021年03月09日 14:14:00
 */
@Component
public class QuartzreflectUtil {

    @SuppressWarnings("all")
    @Autowired
    QuartzService quartzService;

    static Logger logger = LoggerFactory.getLogger(QuartzreflectUtil.class);

    /**
     * 定时任务执行的方法,通过反射调用该类中的方法
     * @param context
     */
    @SuppressWarnings("all")
    public void reflect(JobExecutionContext context){
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        String method = null;
        String cla = null;
        String jobName = null;
        String jobGroupName = null;
        Object[] finalMethodParam = new Object[0];
        try {
            cla = jobDataMap.getString("class");
            jobName = jobDataMap.get("jobName").toString();
            jobGroupName = jobDataMap.get("jobGroupName").toString();
            finalMethodParam = JSONObject.parseObject(JSON.toJSONString(jobDataMap.get("finalMethodParam")),Object[].class);
        }catch (Exception e){
            if (jobName!=null && jobGroupName!=null){
                quartzService.deleteJob(jobName, jobGroupName);
                logger.warn("定时任务方法调用异常,已停止任务   jobName:"+jobName+";\tjobGroupName:"+jobGroupName);
            }else {
                logger.error("定时任务执行错误,无法停止任务,请手动停止任务");
            }

            e.printStackTrace();
        }
        DynamicJob bean = SpringContextUtil.getBean(cla, DynamicJob.class);
        bean.task(finalMethodParam);
    }

    /**
     * 调用定时任务时,需要封装成obj[]才可以使用,工具类整合数据为obj[]
     * @param list          传递过来的list对象数据
     * @param obj               一个限定长度的obj[]对象
     * @param parameterCount            参数长度
     * @return
     */
    @SuppressWarnings("all")
    public static void setObjelement(List list,Object[] obj){
        for (int i = 0; i < list.size(); i++) {                      //通过遍历,往obj2对象数组赋值
            String listClass = list.get(i).getClass().toString();           //得到list当前值的类型
            if (listClass.equals(HashMap.class.toString())) {               //判断类型是否相等。类型相等时,强转类型后复制obj
                obj[i] = (HashMap) list.get(i);
                logger.info("map赋值成功,索引位置:" + i);

            } else if (listClass.equals(String.class.toString())) {
                obj[i] = (String) list.get(i);
                logger.info("String赋值成功,索引位置:" + i);

            } else if (listClass.equals(Integer.class.toString())) {
                obj[i] = (Integer) list.get(i);
                logger.info("Integer赋值成功,索引位置:" + i);
            } else if (listClass.equals(Long.class.toString())) {
                obj[i] = (Long) list.get(i);
                logger.info("Long赋值成功,索引位置:" + i);
            }else if (listClass.equals(HttpServletRequest.class.toString())) {
                obj[i] = (HttpServletRequest) list.get(i);
                logger.info("HttpServletRequest赋值成功,索引位置:" + i);
            }else if (listClass.equals(HttpServletResponse.class.toString())) {
                obj[i] = (HttpServletResponse) list.get(i);
                logger.info("HttpServletResponse赋值成功,索引位置:" + i);
            }else if (listClass.equals(Map.class.toString())) {
                obj[i] = (Map) list.get(i);
                logger.info("Map赋值成功,索引位置:" + i);
            } else if (listClass.equals(LinkedHashMap.class.toString())) {
                obj[i] = (LinkedHashMap) list.get(i);
                logger.info("Map赋值成功,索引位置:" + i);
            }
        }
    }

    /**
     * 传入参数解析
     * @param paramSize         参数长度
     * @param param         map参数
     * @param response          响应对象
     * @param request           请求对象
     * @return          返回的需要调用定时器
     */
    @SuppressWarnings("all")
    public static List inputParamList(Integer paramSize,Map param,HttpServletResponse response,HttpServletRequest request){
        List<Object> list = new ArrayList();
        Map o;
        if (paramSize==0){
            return null;
        }
        for (int i = 1; i <= paramSize; i++) {
            o=(Map) param.get(i+"");
            if (o.containsKey("String")){
                String string = o.get("String").toString();
                list.add(string);
            }else if (o.containsKey("Map")){
                Map map = (Map) o.get("Map");
                list.add(map);
            } else if (o.containsKey("HttpServletResponse")){
                list.add(response);
            }else if (o.containsKey("HttpServletRequest")){
                list.add(request);
            }else if (o.containsKey("Long")){
                Long aLong = Long.valueOf(o.get("Long").toString());
                list.add(aLong);
            }else if (o.containsKey("Integer")){
                Integer integer = Integer.valueOf(o.get("Integer").toString());
                list.add(integer);
            }

        }
        return list;
    }

    /**
     * 传入参数解析
     * @param paramSize         参数长度
     * @param param         map参数
     * @param response          响应对象
     * @param request           请求对象
     * @return          返回的需要调用定时器
     */
    @SuppressWarnings("all")
    public static List inputParamListOptimize(Params param, HttpServletResponse response, HttpServletRequest request){
        List data = null;
        List<Object> list = new ArrayList();
        if(param.getParamData()==null || param.getParamData().size()<=0){
            return null;
        }else {
            data= param.getParamData();
        }

        for (int i = 0; i < data.size(); i++) {
            Map o = (Map) data.get(i);
            if (o.containsKey("String")){
                String string = o.get("String").toString();
                list.add(string);
            }else if (o.containsKey("Map")){
                Map map = (Map) o.get("Map");
                list.add(map);
            } else if (o.containsKey("HttpServletResponse")){
                list.add(response);
            }else if (o.containsKey("HttpServletRequest")){
                list.add(request);
            }else if (o.containsKey("Long")){
                Long aLong = Long.valueOf(o.get("Long").toString());
                list.add(aLong);
            }else if (o.containsKey("Integer")){
                Integer integer = Integer.valueOf(o.get("Integer").toString());
                list.add(integer);
            }
        }
        return list;
    }
}





Quartzservice层代码

  • 接口业务层具体实现方法
package com.example.df.quartz.service;


import com.alibaba.fastjson.JSONObject;
import com.example.df.quartz.entry.Params;
import com.example.df.util.QuartzreflectUtil;
import com.example.df.util.SpringContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author xu
 * @Description
 * @createTime 2021年03月08日 17:32:00
 */
@Service
@Slf4j
public class QuartzService {

    @Qualifier("schedulerFactoryBean")
    @Autowired
    private Scheduler scheduler;

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");


    @PostConstruct
    public void startScheduler() {
        try {
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("all")
    @Transactional
    public Map startaddJob(HttpServletRequest request, HttpServletResponse response, Params param){
        Map<String,Object> res = new HashMap();
        String className = param.getClassName();        //获取需要执行方法的类名
        String jobName = param.getJobName();
        String jobGroupName = param.getJobGroupName();
        String cron = param.getCron();
        if(param.getParamData()!=null && param.getParamData().size()>0){
            param = integratedData(param, response, request);
        }
        Map map = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        map.put("finalMethodParam",param.getFinalMethodParam());
        map.put("class", className);
        Class<?> bean = null;
        try {
            bean = Class.forName(SpringContextUtil.getBean(className).getClass().getCanonicalName());
        } catch (ClassNotFoundException e) {
            log.error("找不到当前类");
            e.printStackTrace();
        }
        String date = addJob(bean, jobName, jobGroupName, cron, map);
        log.info("任务添加成功");
        res.put("msg", date);
        return res;
    }

    /**
     * 增加一个job
     *
     * @param jobClass
     *            任务实现类
     * @param jobName
     *            任务名称
     * @param jobGroupName
     *            任务组名
     * @param jobTime
     *            时间表达式 (这是每隔多少秒为一次任务)
     * @param jobTimes
     *            运行的次数 (<0:表示不限次数)
     */
    @SuppressWarnings("all")
    public String addJob(Class <? extends Job> jobClass, String jobName, String jobGroupName, int jobTime,
                       int jobTimes) {
        String date = null;
        try {
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
                    .build();
            // 使用simpleTrigger规则
            Trigger trigger = null;
            if (jobTimes < 0) {
                trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
                        .startNow().build();
            } else {
                trigger = TriggerBuilder
                        .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
                                .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
                        .startNow().build();
            }
            Date date1 =  scheduler.scheduleJob(jobDetail, trigger);
            date = sdf.format(date1);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return date;
    }
    @SuppressWarnings("all")
    public String addJob(Class jobClass, String jobName, String jobGroupName, String jobTime,Map param) {
        String date = null;

        try {
            // 创建jobDetail实例,绑定Job实现类
            // 指明job的名称,所在组的名称,以及绑定job类
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)
                    .usingJobData(new JobDataMap(param))// 任务名称和组构成任务key
                    .build();
            // 定义调度触发规则
            // 使用cornTrigger规则
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)// 触发器key
                    .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                    .usingJobData(new JobDataMap(param))
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).startNow().build();
            // 把作业和触发器注册到任务调度中
            Date date1 = scheduler.scheduleJob(jobDetail, trigger);
            date = sdf.format(date1);
        } catch (Exception e) {
            date = e.getMessage();
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 增加一个job
     *
     * @param jobClass
     *            任务实现类
     * @param jobName
     *            任务名称
     * @param jobGroupName
     *            任务组名
     * @param jobTime
     *            时间表达式 (如:0/5 * * * * ? )
     */
    @SuppressWarnings("all")
    public String addJob(Class <? extends Job> jobClass, String jobName, String jobGroupName, String jobTime) {
        String date = null;
        try {
            // 创建jobDetail实例,绑定Job实现类
            // 指明job的名称,所在组的名称,以及绑定job类
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
                    .build();
            // 定义调度触发规则
            // 使用cornTrigger规则
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)// 触发器key
                    .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).startNow().build();
            // 把作业和触发器注册到任务调度中
            Date date1 = scheduler.scheduleJob(jobDetail, trigger);
            date = sdf.format(date1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return date;
    }

    /**
     * 修改 一个job的 时间表达式
     *
     * @param jobName   任务名
     * @param jobGroupName  分组名
     * @param jobTime   执行时间
     */
    public void updateJob(String jobName, String jobGroupName, String jobTime) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
            // 重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除任务一个job
     *
     * @param jobName
     *            任务名称
     * @param jobGroupName
     *            任务组名
     */
    public void deleteJob(String jobName, String jobGroupName) {
        try {
            scheduler.deleteJob(new JobKey(jobName, jobGroupName));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 暂停一个job
     *
     * @param jobName   任务名
     * @param jobGroupName  分组名
     */
    public void pauseJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 恢复一个job
     *
     * @param jobName   任务名
     * @param jobGroupName  分组名
     */
    public void resumeJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 立即执行一个job
     *
     * @param jobName       任务名
     * @param jobGroupName  分组名
     */
    public void runAJobNow(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     */
    @SuppressWarnings("all")
    public List<Map<String, Object>> queryAllJob() {
        List<Map<String, Object>> jobList = null;
        try {
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            jobList = new ArrayList<Map<String, Object>>();
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("jobName", jobKey.getName());
                    map.put("jobGroupName", jobKey.getGroup());
                    map.put("description", "触发器:" + trigger.getKey());
                    map.put("className", trigger.getJobDataMap().getString("className"));
                    map.put("classMethod", trigger.getJobDataMap().getString("method"));
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    map.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        map.put("jobTime", cronExpression);
                    }
                    jobList.add(map);
                }
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }

    /**
     * 获取所有正在运行的job
     *
     * @return
     */
    @SuppressWarnings("all")
    public List<Map<String, Object>> queryRunJob() {
        List<Map<String, Object>> jobList = null;
        try {
            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
            jobList = new ArrayList<Map<String, Object>>(executingJobs.size());
            for (JobExecutionContext executingJob : executingJobs) {
                Map<String, Object> map = new HashMap<String, Object>();
                JobDetail jobDetail = executingJob.getJobDetail();
                JobKey jobKey = jobDetail.getKey();
                Trigger trigger = executingJob.getTrigger();
                map.put("jobName", jobKey.getName());
                map.put("jobGroupName", jobKey.getGroup());
                map.put("description", "触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                map.put("jobStatus", triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    map.put("jobTime", cronExpression);
                }
                jobList.add(map);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }

    @SuppressWarnings("all")
    public Params integratedData(Params param, HttpServletResponse response,HttpServletRequest request){         //填充全类名
        List list = QuartzreflectUtil.inputParamListOptimize(param, response, request);
        if (list!=null && list.size()>0){
            Object[] obj2 = new Object[list.size()];             //创建一个以参数长度一样的对象数组
            QuartzreflectUtil.setObjelement(list, obj2);
            param.setFinalMethodParam(obj2);
        }
        return param;
    }

}



QuartzController:

  • 任务的增删改查、执行一次,暂停、重启接口
package com.example.df.quartz.controller;


import com.example.df.quartz.service.QuartzService;
import com.example.df.quartz.entry.Params;
import com.example.df.util.BaseControllerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.Map;


/**
 * @author xu
 * @Description
 * @createTime 2021年03月08日 17:34:00
 */
@RestController
@RequestMapping(value = "/quartz")
public class QuartController{
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @SuppressWarnings("all")
    @Autowired
    QuartzService quartzService;

    @SuppressWarnings("all")
    @RequestMapping("/addjob")
    public void startJob(@Valid @RequestBody Params param, HttpServletResponse response, HttpServletRequest request) {
        Map map = quartzService.startaddJob(request, response, param);
        BaseControllerUtil.renderResult(response, map);
    }

    @SuppressWarnings("all")
    @RequestMapping("/updatejob")
    public void updatejob(@RequestBody Map param, HttpServletResponse response) {
        logger.info("更新任务");
        String jobName = param.get("jobName").toString();
        String jobGroupName = param.get("jobGroupName").toString();
        String cron = param.get("cron").toString();
        quartzService.updateJob(jobName, jobGroupName, cron);
        BaseControllerUtil.renderResult(response, "更新成功");
    }

    @SuppressWarnings("all")
    @RequestMapping("/deletejob")
    public void deletejob(@RequestBody Map param, HttpServletResponse response) {
        logger.info("任务删除");
        String jobName = param.get("jobName").toString();
        String jobGroupName = param.get("jobGroupName").toString();
        quartzService.deleteJob(jobName, jobGroupName);
        BaseControllerUtil.renderResult(response, "删除成功");
    }

    @SuppressWarnings("all")
    @RequestMapping("/pauseJob")
    public void pauseJob(@RequestBody Map param, HttpServletResponse response) {
        logger.info("任务暂停");
        String jobName = param.get("jobName").toString();
        String jobGroupName = param.get("jobGroupName").toString();
        quartzService.pauseJob(jobName, jobGroupName);
        BaseControllerUtil.renderResult(response, "暂停成功");
    }

    @SuppressWarnings("all")
    @RequestMapping("/resumeJob")
    public void resumeJob(@RequestBody Map param, HttpServletResponse response) {
        logger.info("重新开始任务");
        String jobName = param.get("jobName").toString();
        String jobGroupName = param.get("jobGroupName").toString();
        quartzService.resumeJob(jobName, jobGroupName);
        BaseControllerUtil.renderResult(response, "重新开始任务");
    }

    @SuppressWarnings("all")
    @RequestMapping("/queryAllJob")
    public Object queryAllJob() {
        logger.info("查询所有任务");
        return quartzService.queryAllJob();
    }

    @SuppressWarnings("all")
    @RequestMapping("/queryRunJob")
    public Object queryRunJob() {
        logger.info("查询运行中的任务");
        return quartzService.queryRunJob();
    }

    @SuppressWarnings("all")
    @RequestMapping("/RunOnec")
    public void RunOnec(@RequestBody Map param, HttpServletResponse response) {
        logger.info("运行一次任务");
        String jobName = param.get("jobName").toString();
        String jobGroupName = param.get("jobGroupName").toString();
        quartzService.runAJobNow(jobName,jobGroupName);
        BaseControllerUtil.renderResult(response, "运行一次任务");
    }
}


注意!!!!!!!!

**

定义需要执行任务的方法

方法一、定时任务需要执行的方法所在类需要实现DynamicJob<返回值类型(泛型)>接口,并重写接口方法。

SpringBoot整合Quartz定时任务持久化到数据库的开发。超详细,可用_第1张图片

测试代码案例如下:

1、创建一个类实现job接口

TestService

package com.example.df.quartz.service;

import com.example.df.mapper.UserMapper;
import com.example.df.quartz.job.DynamicJob;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author xu
 * @Description
 * @createTime 2021年03月11日 14:37:00
 */
@Component
public class TestService implements DynamicJob<String>
{
    @Autowired
    UserMapper userMapper;

    /**
     * 定时任务执行的方法
     * @param args	可变参数,前端传递,当时无参时为null
     * @return
     */
    @Override
    public String task(Object... args)
    {
        String a =(String) args[0];
        Integer cc =(Integer) args[1];
        Map<String,Object> map = (Map<String,Object>) args[2];
        for (String o : map.keySet()) {
            System.out.println(o+"\t"+map.get(o));
        }
        System.out.println(cc);
        System.out.println(a);
        Integer count = userMapper.getCount();
        System.out.println(count);
        return count.toString();
    }
}


参数传递

添加有参数的方法任务。前端或请求参数格式如下

{
"className":"testService",        需要执行方法的类名(首字母小写,根据spring容器获取对象)
"jobName":"job2",        任务名(随意,可自定义。)
"jobGroupName":"test",        任务分组名(随意,可自定义。)		任务的暂停、执行、删除。。。都是根据任务名和分组名来确定任务
"cron":"0/5 * * * * ?",        执行时间(cron时间表达式)
"paramdata":[			该方法需要接收的参数,根据方法传递
   "String":"test",
   "Map":{"cc":"ss","count":5},
   "Integer":5
  ]	   方法参数数据,"String表示数据类型"	value为具体数据
}

添加无参方法任务。前端或请求参数格式如下

{
"className":"test",
"jobName":"job2",
"jobGroupName":"test",
"cron":"0/5 * * * * ?",					每五秒执行一次
}

执行效果如图所示
SpringBoot整合Quartz定时任务持久化到数据库的开发。超详细,可用_第2张图片

页面展示

根据接口写出自定义的可视化前端页面
在这里插入图片描述

定时任务所需要的所有代码已全部贴上。如有什么问题欢迎评论!

获取最新demo,可前往
giee地址demo

你可能感兴趣的:(任务定时,spring,java,mybatis,quartz,触发器)