Quartz調度核心元素
Scheduler:任務調度器,是實際執行任務調度的控制器。在spring中通過SchedulerFactoryBean封裝起來。
Trigger:觸發器,用于定義任務調度的時間規則,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比較多,本文主要介紹這種方式。CronTrigger在spring中封裝在CronTriggerFactoryBean中。
Calendar:它是一些日歷特定時間點的集合。一個trigger可以包含多個Calendar,以便排除或包含某些時間點。JobDetail:用來描述Job實現類及其它相關的靜態信息,如Job名字、關聯監聽器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean兩種實現,如果任務調度只需要執行某個類的某個方法,就可以通過MethodInvokingJobDetailFactoryBean來調用。
Job:是一個接口,只有一個方法void execute(JobExecutionContext context),開發者實現該接口定義運行任務,JobExecutionContext類提供了調度上下文的各種信息。Job運行時的信息保存在JobDataMap實例中。實現Job接口的任務,默認是無狀態的,若要將Job設置成有狀態的,在quartz中是給實現的Job添加@DisallowConcurrentExecution注解(以前是實現StatefulJob接口,現在已被Deprecated),在與spring結合中可以在spring配置文件的job detail中配置concurrent參數。
Quartz 集群
quartz集群是通過數據庫表來感知其他的應用的,各個節點之間并沒有直接的通信。只有使用持久的JobStore才能完成Quartz集群。
數據庫表:有11張表,表信息如下
image.png
image.png
QRTZ_LOCKS就是Quartz集群實現同步機制的行鎖表,包括以下幾個鎖:CALENDAR_ACCESS 、JOB_ACCESS、MISFIRE_ACCESS 、STATE_ACCESS 、TRIGGER_ACCESS
數據存儲
Quartz 中的 trigger 和 job 需要存儲下來才能被使用。Quartz 中有兩種存儲方式:RAMJobStore, JobStoreSupport,其中 RAMJobStore 是將 trigger 和 job 存儲在內存中,而 JobStoreSupport 是基于 jdbc 將 trigger 和 job 存儲到數據庫中。RAMJobStore 的存取速度非常快,但是由于其在系統被停止后所有的數據都會丟失,所以在通常應用中,都是使用 JobStoreSupport。
在 Quartz 中,JobStoreSupport 使用一個驅動代理來操作 trigger 和 job 的數據存儲:StdJDBCDelegate。StdJDBCDelegate 實現了大部分基于標準 JDBC 的功能接口,但是對于各種數據庫來說,需要根據其具體實現的特點做某些特殊處理,因此各種數據庫需要擴展 StdJDBCDelegate 以實現這些特殊處理。
在Hap中使用的就是JobStoreSupport持久化存儲,下面簡單介紹一下常見幾張表對應的關系:
image.png
這個界面的數據,存放在 qrtz_job_details 表中,但是這里還是有一些需要注意的地方。并不是所有的參數都在這個表中,比如 上次執行時間、下次執行時間、corn表達式等信息,實際上是存在別的表中,這個視 任務的類型來判斷。當簡單任務的時候,存在 qrtz_simple_triggers 表中;當 corn 任務的時候,存在 qrtz_triggers 表中。
image.png
不知道大家有沒有想過這個問題,任務中的參數是放在哪里?我一開始以為會放到一張表里,最后發現參數其實是存在 qrtz_job_details 表里的一個字段上的。
image.png
是一個blob類型。
其實Hap在Job這一塊封裝的東西不算是很多。我們在開發時主要用到的就是一個 AbstractJob 類,之后會介紹到。還有就是任務運行記錄,在Hap中是將任務執行記錄存在 sys_job_running_info 表中,其實也是在 quratz 的監聽器基礎上做到的,之后會有介紹。
配置
applicationContext-job.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- ~ #{copyright}# -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:property-placeholder location="classpath:config.properties"/>
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- DO NOT AUTO STARTUP DURING LOCAL DEV TEST -->
<property name="autoStartup" value="${job.autoStartup}" />
<property name="jobFactory">
<bean class="com.hand.hap.job.AutowiringSpringBeanJobFactory" />
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
<property name="configLocation" value="classpath:quartz.properties" />
</bean>
</beans>
quartz.properties
#============================================================================
# Configure Scheduler
#============================================================================
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.instanceId = AUTO
#org.quartz.scheduler.rmi.export = false
#org.quartz.scheduler.rmi.proxy = false
#org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
org.quartz.scheduler.skipUpdateCheck = true
#============================================================================
# Configure ThreadPool
#============================================================================
#org.quartz.threadPooln.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
#org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.maxMisfiresToHandleAtATime=1
#org.quartz.jobStore.misfireThreshold = 60000
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.scheduler.classLoadHelper.class=org.quartz.simpl.CascadingClassLoadHelper
#org.quartz.jobStore.useProperties = true
#============================================================================
# Microsoft SQL Server
#============================================================================
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
#org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?
#============================================================================
# Configure Plugins
#============================================================================
#org.quartz.plugin.triggHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.runningListener.class=com.hand.hap.job.plugin.RunningListenerPlugin
org.quartz.plugin.runningListener.LogRunningInfo=true
org.quartz.plugin.runningListener.mailTemplate=email_job_running_notification
quratz默認是使用RAMJobStore存儲,這是將信息存儲再內存中,這種方式有一個好處就是速度快,單缺點也很明顯:不能持久化存儲,如果系統出問題 就得從頭開始,所以在項目上我沒一般是采用持久化存儲這種方式。如果需要數據庫持久化存儲,一般需要在quartz.properties配置文件中將存儲方式改成jdbcjobstore,例如:
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
但在Hap中并沒有直接在quartz.properties配置文件中定義數據庫得連接方式,而是在 applicationContext-job.xml 配置文件中將數據源注入進去了。
<context:property-placeholder location="classpath:config.properties"/>
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- DO NOT AUTO STARTUP DURING LOCAL DEV TEST -->
<property name="autoStartup" value="${job.autoStartup}" />
<property name="jobFactory">
<bean class="com.hand.hap.job.AutowiringSpringBeanJobFactory" />
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
<property name="configLocation" value="classpath:quartz.properties" />
</bean>
因為spring 不能在quartz中注入bean的,所以這里自定義了 一個 jobFactory
package com.hand.hap.job;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
/**
* 支持 AutoWired.
*
*/
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
public Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
applicationContextSchedulerContextKey 是 org.springframework.scheduling.quartz.SchedulerFactoryBean這個類中把spring上下 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定義的key得到對應的spring上下文。
Listeners
這是Hap中Job模塊的關鍵部分。Listeners是您創建的對象,用于根據調度程序中發生的事件執行操作。
TriggerListeners和JobListeners
您可能猜到,TriggerListeners接收到與觸發器(trigger)相關的事件,JobListeners 接收與jobs相關的事件。
與觸發相關的事件包括:觸發器觸發,觸發失靈(在本文檔的“觸發器”部分中討論),并觸發完成(觸發器關閉的作業完成)。
org.quartz.TriggerListener接口
public interface TriggerListener {
public String getName();
public void triggerFired(Trigger trigger, JobExecutionContext context);
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);
public void triggerMisfired(Trigger trigger);
public void triggerComplete(Trigger trigger, JobExecutionContext context,
int triggerInstructionCode);
}
job相關事件包括:job即將執行的通知,以及job完成執行時的通知。
org.quartz.JobListener接口
public interface JobListener {
public String getName();
public void jobToBeExecuted(JobExecutionContext context);
public void jobExecutionVetoed(JobExecutionContext context);
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException);
}
使用TriggerListeners和JobListeners
要創建一個listener,只需創建一個實現org.quartz.TriggerListener和/或org.quartz.JobListener接口的對象。然后,listener在運行時會向調度程序注冊,并且必須給出一個名稱(或者,他們必須通過他們的getName()方法來宣傳自己的名字)。
為了方便起見,實現這些接口,您的類也可以擴展JobListenerSupport類或TriggerListenerSupport類,并且只需覆蓋您感興趣的事件。
listener與調度程序的ListenerManager一起注冊,并配有描述listener希望接收事件的job/觸發器的Matcher。
Listener在運行時間內與調度程序一起注冊,并且不與jobs和觸發器一起存儲在JobStore中。這是因為聽眾通常是與應用程序的集成點。因此,每次運行應用程序時,都需要重新注冊該調度程序。
添加對特定job感興趣的JobListener
scheduler.getListenerManager().addJobListener(myJobListener,KeyMatcher.jobKeyEquals(new JobKey(“myJobName”,“myJobGroup”)));
這將上面的例子變成這樣:
scheduler.getListenerManager().addJobListener(myJobListener, jobKeyEquals(jobKey("myJobName", "myJobGroup")));
添加對特定組的所有job感興趣的JobListener:
scheduler.getListenerManager().addJobListener(myJobListener, jobGroupEquals("myJobGroup"));
添加對兩個特定組的所有job感興趣的JobListener:
scheduler.getListenerManager().addJobListener(myJobListener, or(jobGroupEquals("myJobGroup"), jobGroupEquals("yourGroup")));
添加對所有job感興趣的JobListener:
scheduler.getListenerManager().addJobListener(myJobListener, allJobs());
注冊TriggerListeners的工作原理相同。
Quartz的大多數用戶并不使用Listeners,但是當應用程序需求創建需要事件通知時不需要Job本身就必須明確地通知應用程序,這些用戶就很方便。
SchedulerListeners
SchedulerListeners非常類似于TriggerListeners和JobListeners,除了它們在Scheduler本身中接收到事件的通知 (不一定與特定觸發器(trigger)或job相關的事件)
與計劃程序相關的事件包括:添加job/觸發器,刪除job/觸發器,調度程序中的嚴重錯誤,關閉調度程序的通知等。
org.quartz.SchedulerListener接口:
public interface SchedulerListener {
public void jobScheduled(Trigger trigger);
public void jobUnscheduled(String triggerName, String triggerGroup);
public void triggerFinalized(Trigger trigger);
public void triggersPaused(String triggerName, String triggerGroup);
public void triggersResumed(String triggerName, String triggerGroup);
public void jobsPaused(String jobName, String jobGroup);
public void jobsResumed(String jobName, String jobGroup);
public void schedulerError(String msg, SchedulerException cause);
public void schedulerStarted();
public void schedulerInStandbyMode();
public void schedulerShutdown();
public void schedulingDataCleared();
}
SchedulerListeners注冊到調度程序的ListenerManager。SchedulerListeners幾乎可以實現任何實現org.quartz.SchedulerListener接口的對象。
添加SchedulerListener:
scheduler.getListenerManager().addSchedulerListener(mySchedListener);
刪除SchedulerListener:
scheduler.getListenerManager().removeSchedulerListener(mySchedListener);
Hap中的Listener
Hap中得Listener如下:
image.png
主要包括三類:JobListener、TriggerListener、SchedulerListener ;
前面那三個 Default開頭得Listener,沒有什么實質性得內容,只是分別單純得實現了 JobListener、TriggerListener、SchedulerListener 這個三個接口,這樣接下來得哪幾個Listener分別繼承Default開頭得那幾個Listener,就不用是實現JobListener、TriggerListener、SchedulerListener 接口中所有得方法了。這其實就是適配器模式中的 接口適配器模式。至于這幾個適配器里面的邏輯,就不過多說明了。可以簡單看看 SchedulerRunningListener:
/*
* #{copyright}#
*/
package com.hand.hap.job.listener;
import com.hand.hap.job.service.IJobRunningInfoService;
import org.quartz.JobKey;
import org.quartz.listeners.SchedulerListenerSupport;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import com.hand.hap.job.dto.JobRunningInfoDto;
/**
* @author shiliyan
*
*/
public class SchedulerRunningListener extends SchedulerListenerSupport {
private static final String JOB_INFO_HAS_DELETED = "Job Info [{}.{}] has deleted.";
private static final String JOB_WAS_DELETED_FROM_SCHEDULER = "Job [{}.{}] was deleted from Scheduler.";
private final ApplicationContext applicationContext;
public SchedulerRunningListener(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/*
* (non-Javadoc)
*
* @see DefaultSchedulerListener#jobDeleted(org.quartz.JobKey)
*/
@Override
public void jobDeleted(JobKey jobKey) {
JobRunningInfoDto dto = new JobRunningInfoDto();
String group = jobKey.getGroup();
String name = jobKey.getName();
logInfo(JOB_WAS_DELETED_FROM_SCHEDULER, group, name);
dto.setJobName(name);
dto.setJobGroup(group);
deleteJobInfo(dto);
logInfo(JOB_INFO_HAS_DELETED, group, name);
}
private void deleteJobInfo(JobRunningInfoDto jobCreateDto) {
IJobRunningInfoService jobRunningInfoService = applicationContext.getBean(IJobRunningInfoService.class);
jobRunningInfoService.delete(jobCreateDto);
}
protected void logInfo(String info, Object... para) {
if (getLog().isInfoEnabled()) {
getLog().info(info, para);
}
}
protected void logInfo(String info) {
if (getLog().isInfoEnabled()) {
getLog().info(info);
}
}
}
有個 jobDeleted 方法,根據代碼可以看出,在刪除job的時候,會刪除 sys_job_running_info 表中對應Job的執行記錄。
插件SchedulerPlugin
在Hap 中的 quartz.properties 配置文件中,有這樣一行配置
org.quartz.plugin.runningListener.class=com.hand.hap.job.plugin.RunningListenerPlugin
這其實就是quartz的插件功能。
在上面我沒已經提到了Hap中對應Listener類型分別定義了幾個Listener,但是發現還少了點什么。并沒有在哪個地方看到JobRunningListener添加了需要監聽的Job。其實這些動作都在 RunningListenerPlugin 中。
image.png
DefaultSchedulerPlugin 實現了 SchedulerPlugin, 沒什么實質的內容
image.png
RunningListenerPlugin 繼承了 DefaultSchedulerPlugin,并重寫了 start 方法,然后將所有 Job和 JobRunningListener關聯起來
image.png
至此,有關于Hap中Job的內容到此結束。下一章節將介紹 quartz 在 soringboot2 中的應用。
整合Springboot
上一章節介紹了Quartz在Hap中的應用(其實就是在spring 中的應用)。本章節介紹 Quartz 在 springboot2中的應用。
添加依賴
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
</dependency>
有可莪能會報一個 c3p0的錯誤,這時候需要添加一個依賴
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
配置
///QuartzConfigure
package com.hand.sxy.config;
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 pavan.solapure
*/
@Configuration
@EnableScheduling
public class QuartzConfigure {
/**
* 繼承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;
}
}
/**
* 配置任務工廠實例
*
* @param applicationContext spring上下文實例
* @return
*/
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
/**
* 采用自定義任務工廠 整合spring實例來完成構建任務
* see {@link AutowiringSpringBeanJobFactory}
*/
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* 配置任務調度器
* 使用項目數據源作為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;
}
}
//quartz.properties
# 固定前綴org.quartz
# 主要分為scheduler、threadPool、jobStore、plugin等部分
#
#
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
# 實例化ThreadPool時,使用的線程類為SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# threadCount和threadPriority將以setter的形式注入ThreadPool實例
# 并發個數
org.quartz.threadPool.threadCount = 5
# 優先級
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 5000
# 默認存儲在內存中
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties = true
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = qzDS
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/vue?characterEncoding=utf8&autoReconnect=true&useSSL=false
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = root
org.quartz.dataSource.qzDS.maxConnections = 10
使用
//BasalJob
package com.hand.sxy.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* @author spilledyear
*/
public interface BasalJob extends Job {
/**
* Job執行入口
*
* @param context
* @throws JobExecutionException
*/
@Override
void execute(JobExecutionContext context) throws JobExecutionException;
}
//HelloJob
package com.hand.sxy.job.example;
import com.hand.sxy.job.BasalJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/**
* @author spilledyear
*/
public class HelloJob implements BasalJob {
private static Logger logger = LoggerFactory.getLogger(HelloJob.class);
public HelloJob() {
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
logger.info("Hello Job執行時間: " + new Date());
}
}
//JobController
package com.hand.sxy.job.controller;
import com.hand.sxy.job.BasalJob;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author spilledyear
*/
@RestController
@RequestMapping(value = "/job")
public class JobController {
private static Logger log = LoggerFactory.getLogger(JobController.class);
@Autowired
private Scheduler scheduler;
@RequestMapping(value = "/addjob", method = RequestMethod.GET)
public void addjob(@RequestParam(value = "jobGroup") String jobGroup,
@RequestParam(value = "jobName") String jobName,
@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "cronExpression") String cronExpression) throws Exception {
addJob(jobGroup, jobName, jobClassName, cronExpression);
}
public void addJob(String jobGroup, String jobName, String jobClassName, String cronExpression) throws Exception {
// 啟動調度器
scheduler.start();
//構建job信息
JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobGroup, jobName).build();
//表達式調度構建器(即任務執行的時間)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
//按新的cronExpression表達式構建一個新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobGroup, jobName)
.withSchedule(scheduleBuilder).build();
try {
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
System.out.println("創建定時任務失敗" + e);
throw new Exception("創建定時任務失敗");
}
}
.....
}
在前端請求 /job/addJob接口(其實已經集成到了那兩個前后端分離的應用中,前端可視化管理Job)
后臺日志
image.png
數據庫數據
image.png
image.png
前端可視化管理