SpringBoot 整合QUARTZ 并嵌入可视化界面

    在开发中有很多定时任务都不是写死的而是可以人为配置并且写到数据库中的,下面简单的分享一个SpringBoot整合QUARTZ并嵌入可视化界面的demo。

Step1. 在数据库中建立有关quartz的表,我用的是 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;

Step2.新建一个springBoot项目并导入依赖

           pom.xml 文件的内容如下:



	4.0.0

	com.example.demo
	QuartzDemo
	0.0.1-SNAPSHOT
	war

	demo-quartz
	Demo project for quartz

	
		org.springframework.boot
		spring-boot-starter-parent
		2.0.3.RELEASE 
	

	
		UTF-8
		UTF-8
		1.8
	

	
		
		   org.springframework.boot
		   spring-boot-starter
		   
		      
		         org.springframework.boot
		         spring-boot-starter-logging
		      
		   
			
	
		
			org.springframework.boot
			spring-boot-starter-quartz
		

		
			org.springframework.boot
			spring-boot-starter-test
			test
		
		
			org.mybatis.spring.boot
			mybatis-spring-boot-starter
			1.3.2
				
		
            org.springframework.boot
            spring-boot-starter-log4j2
        
		
		    org.apache.logging.log4j
		    log4j-core
		                                    
		
		  com.lmax
		  disruptor
		  3.3.4
		 
		
		  javax.mail
		  mail
		  1.4.7
		 	        		
		
			mysql
			mysql-connector-java
			runtime
		
		
		  com.alibaba
		  fastjson
		  1.2.9
				
		
		  com.alibaba
		  druid
		  1.1.9
		
		
			org.springframework.boot
			spring-boot-starter-web
		
		
		  com.ds.tech
		  dstech3-utility
		  3.1.7
		
		
			org.springframework.boot
			spring-boot-starter-thymeleaf
		
		
			com.aliyun
			aliyun-java-sdk-dysmsapi
			1.1.0
		
		
		
			com.aliyun
			aliyun-java-sdk-core
			3.2.8
		
		
			com.aliyun.oss
			aliyun-sdk-oss
			2.2.1
		 
		
	    	com.github.pagehelper
	   	 	pagehelper
	    	5.0.0
		
	

	
        
            
                src/main/java
                
                    **/*.*
                
                
                    **/*.java
                
            
            
                src/main/resources
                
                    **/*.*
                
            
        	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	



其中下面的这个依赖是Quartz 所必须的

	
			org.springframework.boot
			spring-boot-starter-quartz
		

Step3. 在配置文件中进行配置,application.properties 的内容如下:

#服务端口号
server.port=8082
#以Tomcat为web容器时的字符编码
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.force=true
spring.http.encoding.enabled=true
 
# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/AthenaDB?useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456

#连接池配置
#spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.connectionProperties=characterEncoding=utf8;druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000  
# 合并多个DruidDataSource的监控数据
#spring.datasource.useGlobalDataSourceStat=true 
#spring.datasource.filters=stat,wall,log4j 
#mybaits mapper位置设置
mybatis.mapper-locations=classpath*:com/example/demo/mapper/rmdb/*.xml
#mybatis
mybatis.type-aliases-package=com.example.demo.entity

#访问日志路径
#server.tomcat.accesslog.directory=
#出现错误时, 直接抛出异常,自定义异常页面使用
spring.mvc.throw-exception-if-no-handler-found=true
#不要为我们工程中的资源文件建立映射,自定义异常页面使用
spring.resources.add-mappings=false
#应用名称,一般就是项目名称,这个名称在SpringCloud中比较关键
spring.application.name=dscms
#http请求的字符编码
spring.http.encoding.charset=UTF-8
#配置在使用Thymeleaf做页面模板时的前缀,即页面所在路径
spring.thymeleaf.prefix=classpath:/templates
#设置在使用Thymeleaf做页面模板时的后缀,其实默认就是html
spring.thymeleaf.suffix=.html
#spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.mode=HTML
#设置在使用Thymeleaf做页面模板时是否启用缓存
spring.thymeleaf.cache=false
#设置编码格式
spring.thymeleaf.encoding=UTF-8
#设置静态资源的请求路径
#spring.mvc.static-path-pattern=/**
#指定静态资源的路径
#spring.resources.static-locations=classpath:/static/,classpath:/public/
#禁止对外提供Spring MBeans
spring.jmx.enabled=false

Step4. 在resources文件夹下新建一个 quartz.properties 的配置文件,这样就不会走它自带的quartz.properties的配置文件,配置文件的内容如下:

 
#作业实例名称  
org.quartz.scheduler.instanceName: DefaultQuartzScheduler  
  
#如果使用集群,instanceId必须唯一,设置成AUTO  
org.quartz.scheduler.instanceId = AUTO  
  
org.quartz.scheduler.rmi.export: false  
org.quartz.scheduler.rmi.proxy: false  
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  
#============================================================================  
# Configure JobStore  
#============================================================================  
   
#计划任务存储在内存中
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore  
  
#存储方式使用JobStoreTX(持久化到数据库中)  
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX  
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate  
#使用自己的配置文件  
org.quartz.jobStore.useProperties:true  
#数据库中quartz表的表名前缀  
org.quartz.jobStore.tablePrefix:QRTZ_  
org.quartz.jobStore.dataSource:qzDS  
#是否使用集群(如果项目只部署到 一台服务器,就不用了)  
org.quartz.jobStore.isClustered = false  
   
#============================================================================  
# Configure Datasources  
#============================================================================  
#配置数据源  
org.quartz.dataSource.qzDS.connectionProvider.class:com.example.demo.common.duird.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/AthenaDB?characterEncoding=utf-8
org.quartz.dataSource.qzDS.user:root
org.quartz.dataSource.qzDS.password:123456
org.quartz.dataSource.qzDS.maxConnection:10

Step5. 自定义数据源,因为要持久化到数据库,所以要用ConnectionProvider自定以数据源,代码如下

package com.example.demo.common.duird;


import java.sql.Connection;
import java.sql.SQLException;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;

import com.alibaba.druid.pool.DruidDataSource;

public class DruidConnectionProvider implements ConnectionProvider {

    //JDBC驱动
    public String driver;
    //JDBC连接串
    public String URL;
    //数据库用户名
    public String user;
    //数据库用户密码
    public String password;
    //数据库最大连接数
    public int maxConnection;
    //数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。
    public String validationQuery;
    private boolean validateOnCheckout;
    private int idleConnectionValidationSeconds;
    public String maxCachedStatementsPerConnection;
    private String discardIdleConnectionsSeconds;
    public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
    public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
    //Druid连接池
    private DruidDataSource datasource;
    /*
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    *
    * 接口实现
    *
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    public Connection getConnection() throws SQLException {
        return datasource.getConnection();
    }
    public void shutdown() throws SQLException {
        datasource.close();
    }
    public void initialize() throws SQLException{
        if (this.URL == null) {
            throw new SQLException("DBPool could not be created: DB URL cannot be null");
        }
        if (this.driver == null) {
            throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");
        }
        if (this.maxConnection < 0) {
            throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");
        }
        datasource = new DruidDataSource();
        try{
            datasource.setDriverClassName(this.driver);
        } catch (Exception e) {
            try {
                throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);
            } catch (SchedulerException e1) {
            }
        }
        datasource.setUrl(this.URL);
        datasource.setUsername(this.user);
        datasource.setPassword(this.password);
        datasource.setMaxActive(this.maxConnection);
        datasource.setMinIdle(1);
        datasource.setMaxWait(0);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS);
        if (this.validationQuery != null) {
            datasource.setValidationQuery(this.validationQuery);
            if(!this.validateOnCheckout)
                datasource.setTestOnReturn(true);
            else
                datasource.setTestOnBorrow(true);
            datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
        }
    }
    /*
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    *
    * 提供get set方法
    *
    * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    public String getDriver() {
        return driver;
    }
    public void setDriver(String driver) {
        this.driver = driver;
    }
    public String getURL() {
        return URL;
    }
    public void setURL(String URL) {
        this.URL = URL;
    }
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public int getMaxConnection() {
        return maxConnection;
    }
    public void setMaxConnection(int maxConnection) {
        this.maxConnection = maxConnection;
    }
    public String getValidationQuery() {
        return validationQuery;
    }
    public void setValidationQuery(String validationQuery) {
        this.validationQuery = validationQuery;
    }
    public boolean isValidateOnCheckout() {
        return validateOnCheckout;
    }
    public void setValidateOnCheckout(boolean validateOnCheckout) {
        this.validateOnCheckout = validateOnCheckout;
    }
    public int getIdleConnectionValidationSeconds() {
        return idleConnectionValidationSeconds;
    }
    public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
        this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
    }
    public DruidDataSource getDatasource() {
        return datasource;
    }
    public void setDatasource(DruidDataSource datasource) {
        this.datasource = datasource;
    }
    public String getDiscardIdleConnectionsSeconds() {
        return discardIdleConnectionsSeconds;
    }
    public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {
        this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;
    }

}

注意:上边的这个数据源的内容是和quartz.properties中的数据源的配置互相配合的

Step6. 自定义一个jobfactory ,其目的是在具体的作业中 需要Spring 注入一些Service,所以要自定义一个jobfactory, 让其在具体job 类实例化时 使用Spring 的API 来进行依赖注入

@Component
public class JobFactory extends AdaptableJobFactory {
	
	/**
	 *    让不受spring管理的类具有spring自动注入
	 */
	@Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        // 调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        // 进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

Step7.创建调度器

package com.example.demo.common.config;

import java.io.IOException;
import java.util.Properties;

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
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 com.example.demo.common.factory.JobFactory;


@Configuration
@EnableScheduling
public class SchedulerConfig {


    @Autowired
    JobFactory jobFactory;

	
    @Bean(name="SchedulerFactory")
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        
        //用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 
        //factory.setOverwriteExistingJobs(true);
        
        //QuartzScheduler 延时启动,应用启动完10秒后 QuartzScheduler 再启动 
        //factory.setStartupDelay(10);
 
        factory.setQuartzProperties(quartzProperties());  //作业及数据源配置信息
        
        // 自定义Job Factory,用于Spring注入  service,bin等
        factory.setJobFactory(jobFactory);
    
        return factory;
    }

    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
        //在quartz.properties中的属性被读取并注入后再初始化对象
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    /*
     * quartz初始化监听器
     */
/*    @Bean
    public QuartzInitializerListener executorListener() {
       return new QuartzInitializerListener();
    }*/

    /*
     * 通过SchedulerFactoryBean获取Scheduler的实例
     */
    @Bean(name="Scheduler")
    public Scheduler scheduler() throws IOException {
        return schedulerFactoryBean().getScheduler();
    }
	
}

Step8. 创建一个job_info_config.properties的配置文件,写关于作业的信息

#作业信息配置
demoquartz1_limit=2
demoquartz1_id=1000001
demoquartz1_name="样例作业1";

Step9. 新建一个类读取上面的配置文件中的信息

public class JobInfoConfig {

	/**
	 * 样例作业1的队列查询上限
	 * @return
	 */
	public static Integer getDemoQuartz1Limit() {
		return ConvertUtil.toInteger(ConfigUtil.getSettings("demoquartz1_limit"));
	}
	/**
	 * 样例作业1的id
	 * @return
	 */
	public static String getDemoQuartz1Id() {
		return ConvertUtil.toString(ConfigUtil.getSettings("demoquartz1_id"));
	}
	/**
	 * 样例作业1的名称
	 * @return
	 */
	public static String getDemoQuartz1Name() {
		return ConvertUtil.toString(ConfigUtil.getSettings("demoquartz1_name"));
	}
	
	
	
 
}

Step10, 新建一个job类

package com.example.demo.job;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.example.demo.service.DemoService;


@DisallowConcurrentExecution
public class DemoJob implements IBaseJob {
	
	private final Logger logger = LoggerFactory.getLogger(DemoJob.class);

	
	@Autowired
	private DemoService demoService;

	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		// TODO Auto-generated method stub
		
		try{
			
			demoService.run();
		}catch(Exception e){
			logger.error(e.toString());
		}
		
	}

}
package com.example.demo.job;

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

public interface IBaseJob extends Job {
	
	public void execute(JobExecutionContext context) throws JobExecutionException;
	
}

Step11.在service 中写要执行的作业


/**
 * 读取作业信息
 * @author Administrator
 *
 */
public class BaseService {
	
	/**
	 * 作业编号
	 */
	protected String jobId;
	/**
	 * 作业名称
	 */
	protected String jobName;
    /**
     * 执行成功数量
     */
	protected int success=0;
	/**
	 * 执行失败数量
	 */
	protected int fail = 0;
	/**
	 * 待处理记录总数
	 */
	protected int total = 0;
	
	/**
	 * 设置job编号
	 * @param jid
	 */
	protected void setJobId(String jId) {
		jobId = jId;
	}
	/**
	 * 设置job名称
	 * @param jName
	 */
	protected void setJobName(String jName) {
		jobName = jName;
	}
	
	/**
	 * 开始作业方法
	 */
	protected void startJob() {
		success = 0;
		fail=0;
		total = 0;
		LogWriter.writeJobStartLog(jobId, jobName);
	}
	/**
	 * 设置作业待处理总数
	 * @param tl
	 */
	protected void setJobTotal(int tl) {
		total = tl;
		LogWriter.writeJobStartLog(jobId, jobName,total);
	}
	/**
	 * 当前项处理状态
	 * @param currentKey
	 */
	protected void setSuccessJob(String currentKey) {
		success++;
		String msg = String.format("jobsrun - jobid:%s jobname:%s key:%s status:success", jobId,jobName,currentKey);
		LogWriter.writeWorkLog(msg);
	}
	/**
	 * 失败处理
	 * @param currentKey
	 */
	protected void setFailJob(String currentKey) {
		fail++;
		String msg = String.format("jobsrun - jobid:%s jobname:%s key:%s status:fail", jobId,jobName,currentKey);
		LogWriter.writeWorkLog(msg);
	}
	
	/**
	 * 结束作业
	 */
	protected void endJob() {
		LogWriter.writeJobEndLog(jobId, jobName, total, success, fail);
	}
}
package com.example.demo.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.common.config.JobInfoConfig;
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import com.example.demo.entity.UserExample;



@Service
public class DemoService extends BaseService{
	
	private final Logger logger = LoggerFactory.getLogger(DemoService.class);

	@Autowired
	private UserDao userDao;
	
	public void run(){
		//设置作业编号
		setJobId(JobInfoConfig.getDemoQuartz1Id());
		System.out.println("作业编号:"+JobInfoConfig.getDemoQuartz1Name());
		//设置作业名称
		setJobName(JobInfoConfig.getDemoQuartz1Name());
		System.out.println("作业名称为:"+JobInfoConfig.getDemoQuartz1Name());
		//开始作业
		startJob();
		//获取待处理队列
		List list = getQueueList();
		if(list.size() == 0){
			endJob();
			return;
		}
		//设置待处理的总数
		setJobTotal(list.size());

		//循环处理内容
		for (User user : list) {
			queueToDeal(user);
		}
		//结束作业
		endJob();
	}
	
	public List getQueueList(){
		
		List list = new ArrayList();
		
		try{
			
			UserExample userExample = new UserExample();
			userExample.createCriteria().andBalanceEqualTo(new BigDecimal(0));
			userExample.setStart(0);
			userExample.setLength(Integer.valueOf(JobInfoConfig.getDemoQuartz1Limit()));
			System.out.println("作业上限:"+JobInfoConfig.getDemoQuartz1Limit());
			list = userDao.selectByExample(userExample);
			
		}catch(Exception e){
			logger.error(jobName,e);
		}
		
		return list;
		
	}
	
	
	public void queueToDeal(User user){
		
		User users = userDao.selectByPrimaryKey(user.getUserId());
		
		try{
			
			if(users != null){
				users.setBalance(new BigDecimal(1));
				UserExample userExample = new UserExample();
				userExample.createCriteria().andUserIdEqualTo(user.getUserId());
				
				if(userDao.updateByExample(users, userExample)<=0){
					setFailJob(users.getUserId().toString());
					return;
				}
				setSuccessJob(users.getUserId().toString());
			
			}else{
				
				setFailJob(user.getUserId().toString());
				return;
			}
			
		}catch(Exception e){
			setFailJob(users.getUserId().toString());
			logger.error(e.toString());
		}
		
	}

}
package com.example.demo.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.dao.JobAndTriggerDao;
import com.example.demo.entity.JobAndTrigger;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

/**
 * 作业配置服务
 * 查询所有的作业
 */
@Service
public class JobAndTriggerSevice {

	@Autowired
	private JobAndTriggerDao jobAndTriggerDao;
	
	/**
	 * 分页查询
	 * @param pageNum
	 * @param pageSize
	 * @return
	 */
	public PageInfo getJobAndTriggerDetails(int pageNum, int pageSize) {
		PageHelper.startPage(pageNum, pageSize);
		List list = jobAndTriggerDao.getJobAndTriggerDetails();
		PageInfo page = new PageInfo(list);
		return page;
	}

}

 

 Step12. controller 中的代码如下:

package com.example.demo.controller;

import java.util.HashMap;
import java.util.Map;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.ds.tech.utility.log4j2.LogWriter;
import com.example.demo.entity.JobAndTrigger;
import com.example.demo.job.IBaseJob;
import com.example.demo.service.JobAndTriggerSevice;
import com.github.pagehelper.PageInfo;

@RestController
@RequestMapping(value="/job")
public class JobController {
	
	@Autowired
	private JobAndTriggerSevice jobAndTriggerSevice;
   //加入Qulifier注解,通过名称注入bean
    @Autowired 
    @Qualifier("Scheduler")
    Scheduler scheduler;
    
    public static IBaseJob getClass(String classname) throws Exception 
    {
        Class class1 = Class.forName(classname);
        return (IBaseJob)class1.newInstance();
    }
    
    @PostMapping(value="/addjob")
    public void addJob(@RequestParam(value="jobClassName")String jobClassName, 
			@RequestParam(value="jobGroupName")String jobGroupName, 
			@RequestParam(value="cronExpression")String cronExpression,
			@RequestParam(value="description")String description) throws Exception {
    	   // 启动调度器  
        scheduler.start();
        //构建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName, jobGroupName).withDescription(description).build();
         
        //表达式调度构建器(即任务执行的时间)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

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

        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            System.out.println("创建定时任务失败"+e);
            //throw new Exception("创建定时任务失败");
        }
    	
    }
    
    @PostMapping(value="/pausejob")
    public void pauseJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception {

        try {
        	
            scheduler.pauseJob(JobKey.jobKey(jobClassName, jobGroupName));
        } catch (SchedulerException e) {
            System.out.println("停止定时任务失败"+e);
            //throw new Exception("创建定时任务失败");
        }
    	
    }
    
    @PostMapping(value="/resumejob")
    public void resumeJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception {

        try {
        	
            scheduler.resumeJob(JobKey.jobKey(jobClassName, jobGroupName));
        } catch (SchedulerException e) {
            System.out.println("继续定时任务失败"+e);
            //throw new Exception("创建定时任务失败");
        }
    	
    }
    
    @PostMapping(value="/deletejob")
    public void deleteJob(@RequestParam(value="jobClassName")String jobClassName, @RequestParam(value="jobGroupName")String jobGroupName) throws Exception {

        try {
        	scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));
			scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));
            scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));
        } catch (SchedulerException e) {
            System.out.println("删除定时任务失败"+e);
            //throw new Exception("创建定时任务失败");
        }
    	
    }
    
    /**
     * 查询任务列表
     * @param pageNum
     * @param pageSize
     * @return
     */
	@GetMapping(value="/queryjob")
	public Map queryjob(@RequestParam(value="pageNum")Integer pageNum, @RequestParam(value="pageSize")Integer pageSize) 
	{	
		Map map = new HashMap();
		
		try {
			
			PageInfo jobAndTrigger = jobAndTriggerSevice.getJobAndTriggerDetails(pageNum, pageSize);
			map.put("JobAndTrigger", jobAndTrigger);
			map.put("number", jobAndTrigger.getTotal());
			
		} catch (Exception e) {
			LogWriter.writeErrorLog("查询定时任务列表失败", e);
		}
 
		return map;
	}
	
	/**
	 * 修改定时任务
	 * @param jobClassName
	 * @param jobGroupName
	 * @param cronExpression
	 * @param description
	 * @throws Exception
	 */
    @PostMapping(value="/reschedulejob")
	public void rescheduleJob(@RequestParam(value="jobClassName")String jobClassName, 
			@RequestParam(value="jobGroupName")String jobGroupName,
			@RequestParam(value="cronExpression")String cronExpression,
			@RequestParam(value="description")String description) throws Exception
	{			
    	try {
			TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);
			// 表达式调度构建器
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

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

			// 按新的cronExpression表达式重新构建trigger
			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).withDescription(description).build();

			// 按新的trigger重新设置job执行
			scheduler.rescheduleJob(triggerKey, trigger);
		} catch (SchedulerException e) {
			LogWriter.writeErrorLog("更新定时任务失败", e);
		}
	}
	
}

Step13. 放开页面所在的静态资源,让他可以访问到

package com.example.demo.common.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Spring mvc 配置
 *
 */
@Configuration
@EnableWebMvc
@ComponentScan
public class WebMvcConfig implements WebMvcConfigurer {
 
	/**
	 * 设置资源
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		//全部静态资源,如果指定全部资源则全局异常捕获无法传递到自定义的error.html页面
		//registry.addResourceHandler("/webapp/**").addResourceLocations("classpath:/webapp/"); 
		registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
		WebMvcConfigurer.super.addResourceHandlers(registry);
	}
 
}

Step14. 在static 文件夹下建立一个html,代码如下:





	Quartz任务管理
	
	
	
	
	
	
	
	


    
查询 添加

© Quartz 任务管理

这样所有的东西都完成啦,运行一下就好啦,运行效果如下:

SpringBoot 整合QUARTZ 并嵌入可视化界面_第1张图片

 

你可能感兴趣的:(QUARTZ,springBoo)