javascript
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 文件的內容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example.demo</groupId><artifactId>QuartzDemo</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>demo-quartz</name><description>Demo project for quartz</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.3.RELEASE</version> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId></dependency> <dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.3.4</version></dependency> <dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version></dependency> <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.9</version></dependency> <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.9</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.ds.tech</groupId><artifactId>dstech3-utility</artifactId><version>3.1.7</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>1.1.0</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>3.2.8</version></dependency><dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>2.2.1</version></dependency> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.0.0</version></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><includes><include>**/*.*</include></includes><excludes><exclude>**/*.java</exclude></excludes></resource><resource><directory>src/main/resources</directory><includes><include>**/*.*</include></includes></resource></resources> <plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>其中下面的這個依賴是Quartz 所必須的
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency>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=falseStep4. 在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:10Step5. 自定義數據源,因為要持久化到數據庫,所以要用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);elsedatasource.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自動注入*/@Autowiredprivate AutowireCapableBeanFactory capableBeanFactory;@Overrideprotected 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 {@AutowiredJobFactory 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;}@Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));//在quartz.properties中的屬性被讀取并注入后再初始化對象propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}/** quartz初始化監聽器*/ /* @Beanpublic 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);@Autowiredprivate DemoService demoService;@Overridepublic void execute(JobExecutionContext context)throws JobExecutionException {// TODO Auto-generated method stubtry{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);@Autowiredprivate UserDao userDao;public void run(){//設置作業編號setJobId(JobInfoConfig.getDemoQuartz1Id());System.out.println("作業編號:"+JobInfoConfig.getDemoQuartz1Name());//設置作業名稱setJobName(JobInfoConfig.getDemoQuartz1Name());System.out.println("作業名稱為:"+JobInfoConfig.getDemoQuartz1Name());//開始作業startJob();//獲取待處理隊列List<User> list = getQueueList();if(list.size() == 0){endJob();return;}//設置待處理的總數setJobTotal(list.size());//循環處理內容for (User user : list) {queueToDeal(user);}//結束作業endJob();}public List<User> getQueueList(){List<User> list = new ArrayList<User>();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 {@Autowiredprivate JobAndTriggerDao jobAndTriggerDao;/*** 分頁查詢* @param pageNum* @param pageSize* @return*/public PageInfo<JobAndTrigger> getJobAndTriggerDetails(int pageNum, int pageSize) {PageHelper.startPage(pageNum, pageSize);List<JobAndTrigger> list = jobAndTriggerDao.getJobAndTriggerDetails();PageInfo<JobAndTrigger> page = new PageInfo<JobAndTrigger>(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 {@Autowiredprivate 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表達式構建一個新的triggerCronTrigger 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<String, Object> queryjob(@RequestParam(value="pageNum")Integer pageNum, @RequestParam(value="pageSize")Integer pageSize) { Map<String, Object> map = new HashMap<String, Object>();try {PageInfo<JobAndTrigger> 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表達式重新構建triggertrigger = 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 {/*** 設置資源*/@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {//全部靜態資源,如果指定全部資源則全局異常捕獲無法傳遞到自定義的error.html頁面//registry.addResourceHandler("/webapp/**").addResourceLocations("classpath:/webapp/"); registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");WebMvcConfigurer.super.addResourceHandlers(registry);}}Step14. 在static 文件夾下建立一個html,代碼如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"><title>Quartz任務管理</title><link rel="stylesheet" href="https://unpkg.com/element-ui@2.0.5/lib/theme-chalk/index.css"><script src="https://unpkg.com/vue/dist/vue.js"></script><script src="https://cdn.bootcss.com/vue-validator/2.1.5/vue-validator.js"></script><script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script><script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script><style> #top {background:#20A0FF;padding:5px;overflow:hidden}</style></head> <body><div id="test"> <div id="top"> <el-button type="text" @click="search" style="color:white">查詢</el-button> <el-button type="text" @click="handleadd" style="color:white">添加</el-button> </span> </div> <br/><div style="margin-top:15px"> <el-tableref="testTable" :data="tableData"style="width:100%"border><el-table-columnprop="job_NAME"label="任務名稱"sortableshow-overflow-tooltip></el-table-column><el-table-columnprop="job_GROUP"label="任務所在組"width="120"sortable></el-table-column><el-table-columnprop="job_CLASS_NAME"label="任務類名"sortable></el-table-column><el-table-columnprop="trigger_NAME"label="觸發器名稱"sortable></el-table-column><el-table-columnprop="trigger_GROUP"label="觸發器所在組"width="120"sortable></el-table-column><el-table-columnprop="cron_EXPRESSION"label="表達式"sortable></el-table-column><el-table-columnprop="time_ZONE_ID"label="時區"sortable></el-table-column><el-table-column label="操作" width="300"><template scope="scope"><el-buttonsize="small"type="warning"@click="handlePause(scope.$index, scope.row)">暫停</el-button><el-buttonsize="small"type="info"@click="handleResume(scope.$index, scope.row)">恢復</el-button><el-buttonsize="small"type="danger"@click="handleDelete(scope.$index, scope.row)">刪除</el-button><el-buttonsize="small"type="success"@click="handleUpdate(scope.$index, scope.row)">修改</el-button></template></el-table-column></el-table><div align="center"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="[10, 20, 30, 40]":page-size="pagesize"layout="total, sizes, prev, pager, next, jumper":total="totalCount"></el-pagination></div></div> <el-dialog title="添加任務" :visible.sync="dialogFormVisible" width="500px"><el-form :model="form"><el-form-item label="任務名稱" label-width="120px" style="width:400px"><el-input v-model="form.jobName" auto-complete="off"></el-input></el-form-item><el-form-item label="任務分組" label-width="120px" style="width:400px"><el-input v-model="form.jobGroup" auto-complete="off"></el-input></el-form-item><el-form-item label="表達式" label-width="120px" style="width:400px"><el-input v-model="form.cronExpression" auto-complete="off"></el-input></el-form-item><el-form-item label="備注信息" label-width="120px" style="width:400px"><el-input type="textarea" v-model="form.description" auto-complete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogFormVisible = false">取 消</el-button><el-button type="primary" @click="add">確 定</el-button></div></el-dialog><el-dialog title="修改任務" :visible.sync="updateFormVisible" width="500px"><el-form :model="updateform"><el-form-item label="表達式" label-width="120px" style="width:400px"><el-input v-model="updateform.cronExpression" auto-complete="off"></el-input></el-form-item><el-form-item label="備注信息" label-width="120px" style="width:400px"><el-input type="textarea" v-model="form.description" auto-complete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="updateFormVisible = false">取 消</el-button><el-button type="primary" @click="update">確 定</el-button></div></el-dialog></div><footer align="center"><p>© Quartz 任務管理</p></footer><script>var vue = new Vue({ el:"#test",data: { //表格當前頁數據tableData: [],//請求的URLurl:'job/queryjob',//默認每頁數據量pagesize: 10, //當前頁碼currentPage: 1,//查詢的頁碼start: 1,//默認數據總數totalCount: 1000,//添加對話框默認可見性dialogFormVisible: false,//修改對話框默認可見性updateFormVisible: false,//提交的表單form: {jobName: '',jobGroup: '',cronExpression: '',description:'',},updateform: {jobName: '',jobGroup: '',cronExpression: '',description:'',},},methods: {//從服務器讀取數據loadData: function(pageNum, pageSize){ this.$http.get('job/queryjob?' + 'pageNum=' + pageNum + '&pageSize=' + pageSize).then(function(res){console.log(res)this.tableData = res.body.JobAndTrigger.list;this.totalCount = res.body.number;},function(){console.log('failed');}); }, //單行刪除handleDelete: function(index, row) {this.$http.post('job/deletejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){this.loadData( this.currentPage, this.pagesize);},function(){console.log('failed');});},//暫停任務handlePause: function(index, row){this.$http.post('job/pausejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){this.loadData( this.currentPage, this.pagesize);},function(){console.log('failed');});},//恢復任務handleResume: function(index, row){this.$http.post('job/resumejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){this.loadData( this.currentPage, this.pagesize);},function(){console.log('failed');});},//搜索search: function(){this.loadData(this.currentPage, this.pagesize);},//彈出對話框handleadd: function(){ this.dialogFormVisible = true; },//添加add: function(){this.$http.post('job/addjob',{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression,"description":this.form.description},{emulateJSON: true}).then(function(res){this.loadData(this.currentPage, this.pagesize);this.dialogFormVisible = false;},function(){console.log('failed');});this.form.jobName="";},//更新handleUpdate: function(index, row){console.log(row)this.updateFormVisible = true;this.updateform.jobName = row.job_CLASS_NAME;this.updateform.jobGroup = row.job_GROUP;},//更新任務update: function(){this.$http.post('job/reschedulejob',{"jobClassName":this.updateform.jobName,"jobGroupName":this.updateform.jobGroup,"cronExpression":this.updateform.cronExpression,"description":this.form.description},{emulateJSON: true}).then(function(res){this.loadData(this.currentPage, this.pagesize);this.updateFormVisible = false;},function(){console.log('failed');});},//每頁顯示數據量變更handleSizeChange: function(val) {this.pagesize = val;this.loadData(this.currentPage, this.pagesize);},//頁碼變更handleCurrentChange: function(val) {this.currentPage = val;this.loadData(this.currentPage, this.pagesize);}, }, });//載入數據vue.loadData(vue.currentPage, vue.pagesize);</script> </body> </html>這樣所有的東西都完成啦,運行一下就好啦,運行效果如下:
?
總結
以上是生活随笔為你收集整理的SpringBoot 整合QUARTZ 并嵌入可视化界面的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 弹窗炸弹及其解除
- 下一篇: 带SN切换流程_一文详解贴片电阻生产工艺
