可視化定時(shí)任務(wù)quartz集成解析全過程
前言
在日常的工作中,定時(shí)任務(wù)也是一個(gè)非常常見的需求,可以使用注解實(shí)現(xiàn),但是使用這種方式有幾個(gè)問題,首先就是如果修改比較麻煩,而且沒有提供特定的頁面對(duì)定時(shí)任務(wù)進(jìn)行可視化管理。所以quartz就應(yīng)運(yùn)而生。本文將介紹如何實(shí)現(xiàn)springboot與quartz的整合。
1.目錄結(jié)構(gòu)

配置類 配置SchedulerFactory固化定時(shí)任務(wù)和日志的業(yè)務(wù)類定時(shí)任務(wù)類運(yùn)行配置
2.原理

- scheduler是一個(gè)計(jì)劃調(diào)度器容器(總部),容器里面可以盛放眾多的JobDetail和trigger,當(dāng)容器啟動(dòng)后,里面的每個(gè)JobDetail都會(huì)根據(jù)trigger按部就班自動(dòng)去執(zhí)行。
- JobDetail是一個(gè)可執(zhí)行的工作,它本身可能是有狀態(tài)的。
- Trigger代表一個(gè)調(diào)度參數(shù)的配置,什么時(shí)候去調(diào)。
- 當(dāng)JobDetail和Trigger在scheduler容器上注冊(cè)后,形成了裝配好的作業(yè)(JobDetail和Trigger所組成的一對(duì)兒),就可以伴隨容器啟動(dòng)而調(diào)度執(zhí)行了。
- scheduler是個(gè)容器,容器中有一個(gè)線程池,用來并行調(diào)度執(zhí)行每個(gè)作業(yè),這樣可以提高容器效率
3.表結(jié)構(gòu)
自帶有11張系統(tǒng)表和2張業(yè)務(wù)表(自建),其中綠色為自己建立的表

4.整合springboot
1.pom文件
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>2.ScheduleConfig
配置工廠類的參數(shù)。
@Configuration
public class ScheduleConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
// quartz參數(shù)
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "QUARTZ");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
// 線程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
// JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
// 集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
// sqlserver 啟用
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
factory.setQuartzProperties(prop);
factory.setSchedulerName("QUARTZ");
// 延時(shí)啟動(dòng)
factory.setStartupDelay(1);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
// 可選,QuartzScheduler
// 啟動(dòng)時(shí)更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對(duì)應(yīng)記錄了
factory.setOverwriteExistingJobs(true);
// 設(shè)置自動(dòng)啟動(dòng),默認(rèn)為true
factory.setAutoStartup(true);
return factory;
}
}3.業(yè)務(wù)類
主要是為頁面提供接口。
@RestController
@RequestMapping("/qrtzJob")
public class QrtzJobController {
@Autowired
private QrtzJobService qrtzJobService;
/**
* 檢查表達(dá)式
*
* @param cron
* @return
*/
@GetMapping("/check")
public boolean checkCron(String cron) {
try {
return CronExpression.isValidExpression(cron);
} catch (Exception e) {
return false;
}
}
/**
* 通過對(duì)象查詢數(shù)據(jù)
*
* @param qrtzJobVO 請(qǐng)求對(duì)象
* @return 數(shù)據(jù)集合
*/
@GetMapping("/selectQrtzJob")
@ApiOperation(value = "通過實(shí)體查詢", notes = "通過實(shí)體查詢")
public Result<List<QrtzJobVO>>
selectQrtzJob(@ApiParam(name = "入?yún)?duì)象", value = "實(shí)體", required = false) QrtzJobVO qrtzJobVO) {
Result<List<QrtzJobVO>> resultVO = new Result<>();
// 業(yè)務(wù)查詢
List<QrtzJobVO> resultListQrtzJob = qrtzJobService.queryQrtzJobAll(qrtzJobVO);
resultVO.setData(resultListQrtzJob);
return resultVO;
}
/**
* 通過對(duì)象增加數(shù)據(jù)
*
* @param qrtzJobVO 請(qǐng)求對(duì)象
* @return
*/
@PostMapping("/createQrtzJob")
@ApiOperation(value = "通過實(shí)體添加", notes = "通過實(shí)體添加")
public Result
createQrtzJob(@ApiParam(name = "入?yún)?duì)象", value = "實(shí)體", required = false) @RequestBody QrtzJobVO qrtzJobVO) {
Result resultVO = new Result();
// 業(yè)務(wù)處理
qrtzJobService.createScheduleJob(qrtzJobVO);
return resultVO;
}
/**
* 通過對(duì)象修改
*
* @param qrtzJobVO 請(qǐng)求對(duì)象
* @return
*/
@PutMapping("/updateQrtzJob")
@ApiOperation(value = "通過實(shí)體修改", notes = "通過實(shí)體修改")
public Result
updateQrtzJob(@ApiParam(name = "入?yún)?duì)象", value = "實(shí)體", required = false) @RequestBody QrtzJobVO qrtzJobVO) {
Result resultVO = new Result();
// 業(yè)務(wù)處理
qrtzJobService.updateScheduleJob(qrtzJobVO);
return resultVO;
}
/**
* 通過對(duì)象刪除
*
* @param qrtzJobVO 請(qǐng)求對(duì)象
* @return
*/
@DeleteMapping("/deleteQrtzJob")
@ApiOperation(value = "通過實(shí)體刪除", notes = "通過實(shí)體刪除")
public Result
deleteQrtzJob(@ApiParam(name = "入?yún)?duì)象", value = "實(shí)體", required = false) @RequestBody QrtzJobVO qrtzJobVO) {
Result resultVO = new Result();
// 業(yè)務(wù)處理
qrtzJobService.deleteScheduleJob(qrtzJobVO);
return resultVO;
}
/**
* 運(yùn)行定時(shí)任務(wù)
*
* @param qrtzJobVO
* @return
*/
@GetMapping("/runJob")
public Result
runJob(@ApiParam(name = "入?yún)?duì)象", value = "實(shí)體", required = false) @RequestBody List<QrtzJobVO> qrtzJobVO) {
Result resultVO = new Result();
// 業(yè)務(wù)處理
qrtzJobService.run(qrtzJobVO);
return resultVO;
}
/**
* 暫停定時(shí)任務(wù)
*
* @param qrtzJobVO
* @return
*/
@GetMapping("/pauseJob")
public Result
pauseJob(@ApiParam(name = "入?yún)?duì)象", value = "實(shí)體", required = false) @RequestBody List<QrtzJobVO> qrtzJobVO) {
Result resultVO = new Result();
// 業(yè)務(wù)處理
qrtzJobService.pause(qrtzJobVO);
return resultVO;
}
@GetMapping("resumeJob")
public Result
resumeJob(@ApiParam(name = "入?yún)?duì)象", value = "實(shí)體", required = false) @RequestBody List<QrtzJobVO> qrtzJobVO) {
Result resultVO = new Result();
// 業(yè)務(wù)處理
qrtzJobService.resume(qrtzJobVO);
return resultVO;
}
}4.運(yùn)行配置
ScheduleUtils:將新添加的trigger,jobDetail放入scheduler中。
public class ScheduleUtils {
private static final Logger log = LoggerFactory.getLogger(ScheduleUtils.class);
private static final String JOB_NAME_PREFIX = "TASK_";
private static TriggerKey getTriggerKey(Long jobId) {
return TriggerKey.triggerKey("TASK_" + jobId);
}
private static JobKey getJobKey(Long jobId) {
return JobKey.jobKey("TASK_" + jobId);
}
public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {
try {
return (CronTrigger)scheduler.getTrigger(getTriggerKey(jobId));
} catch (SchedulerException e) {
log.error("獲取Cron表達(dá)式失敗", e);
return null;
}
}
public static void createScheduleJob(Scheduler scheduler, QrtzJobVO scheduleJob) {
try {
JobDetail jobDetail = JobBuilder.newJob(com.job.util.ScheduleJob.class)
.withIdentity(getJobKey(scheduleJob.getJobId())).build();
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = (CronTrigger)TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder).build();
jobDetail.getJobDataMap().put("JOB_PARAM_KEY", scheduleJob);
scheduler.scheduleJob(jobDetail, trigger);
if (scheduleJob.getStatus().equals(CommonConstant.QUARTZ_PAUSE)) {
pauseJob(scheduler, scheduleJob.getJobId());
}
} catch (SchedulerException e) {
log.error("創(chuàng)建定時(shí)任務(wù)失敗", e);
}
}
public static void updateScheduleJob(Scheduler scheduler, QrtzJobVO scheduleJob) {
try {
TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId());
if (trigger == null) {
throw new SchedulerException("獲取Cron表達(dá)式失敗");
}
trigger =
(CronTrigger)trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
trigger.getJobDataMap().put("JOB_PARAM_KEY", scheduleJob);
scheduler.rescheduleJob(triggerKey, trigger);
if (scheduleJob.getStatus().equals(CommonConstant.QUARTZ_PAUSE)) {
pauseJob(scheduler, scheduleJob.getJobId());
}
} catch (SchedulerException e) {
log.error("更新定時(shí)任務(wù)失敗", e);
}
}
public static void run(Scheduler scheduler, QrtzJobVO scheduleJob) {
try {
JobDataMap dataMap = new JobDataMap();
dataMap.put("JOB_PARAM_KEY", scheduleJob);
scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
} catch (SchedulerException e) {
log.error("執(zhí)行定時(shí)任務(wù)失敗", e);
}
}
public static void pauseJob(Scheduler scheduler, Long jobId) {
try {
scheduler.pauseJob(getJobKey(jobId));
} catch (SchedulerException e) {
log.error("暫停定時(shí)任務(wù)失敗", e);
}
}
public static void resumeJob(Scheduler scheduler, Long jobId) {
try {
scheduler.resumeJob(getJobKey(jobId));
} catch (SchedulerException e) {
log.error("恢復(fù)定時(shí)任務(wù)失敗", e);
}
}
public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
try {
scheduler.deleteJob(getJobKey(jobId));
} catch (SchedulerException e) {
log.error("刪除定時(shí)任務(wù)失敗", e);
}
}
}5.ScheduleJob
用于執(zhí)行定時(shí)任務(wù)。
public class ScheduleJob extends QuartzJobBean {
private static final Logger log = LoggerFactory.getLogger(ScheduleJob.class);
private ExecutorService service = Executors.newSingleThreadExecutor();
@Override
protected void executeInternal(JobExecutionContext context) {
QrtzJobVO scheduleJob = (QrtzJobVO)context.getMergedJobDataMap().get("JOB_PARAM_KEY");
QrtzJobLogService scheduleJobLogService = (QrtzJobLogService) SpringContextUtil.getBean(QrtzJobLogService.class);
QrtzJobLogVO jobLog = new QrtzJobLogVO();
jobLog.setJobId(scheduleJob.getJobId());
jobLog.setBeanName(scheduleJob.getBeanName());
jobLog.setMethodName(scheduleJob.getMethodName());
if (CommonUtil.isEmpty(scheduleJob.getParams())) {
jobLog.setParams("");
} else {
jobLog.setParams(scheduleJob.getParams());
}
long startTime = System.currentTimeMillis();
try {
log.info("任務(wù)準(zhǔn)備執(zhí)行,任務(wù)ID:{}", scheduleJob.getJobId());
ScheduleRunnable task =
new ScheduleRunnable(scheduleJob.getBeanName(), scheduleJob.getMethodName(), scheduleJob.getParams());
Future<?> future = this.service.submit(task);
future.get();
long times = System.currentTimeMillis() - startTime;
jobLog.setTimes(Long.valueOf(times));
jobLog.setStatus("0");
log.info("任務(wù)執(zhí)行完畢,任務(wù)ID:{} 總共耗時(shí):{} 毫秒", scheduleJob.getJobId(), Long.valueOf(times));
} catch (Exception e) {
log.error("任務(wù)執(zhí)行失敗,任務(wù)ID:" + scheduleJob.getJobId(), e);
long times = System.currentTimeMillis() - startTime;
jobLog.setTimes(Long.valueOf(times));
jobLog.setStatus("1");
jobLog.setError(StringUtils.substring(e.toString(), 0, 2000));
} finally {
scheduleJobLogService.insertQrtzJobLog(jobLog);
}
}
}6.ScheduleRunnable
通過反射回去需要調(diào)用的類與方法。
public class ScheduleRunnable implements Runnable {
private static final Logger log = LoggerFactory.getLogger(ScheduleRunnable.class);
private Object target;
private Method method;
private String params;
ScheduleRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException {
this.target = SpringContextUtil.getBean(beanName);
this.params = params;
if (StringUtils.isNotBlank(params)) {
this.method = this.target.getClass().getDeclaredMethod(methodName, new Class[] {String.class});
} else {
this.method = this.target.getClass().getDeclaredMethod(methodName, new Class[0]);
}
}
@Override
public void run() {
try {
ReflectionUtils.makeAccessible(this.method);
if (StringUtils.isNotBlank(this.params)) {
this.method.invoke(this.target, new Object[] {this.params});
} else {
this.method.invoke(this.target, new Object[0]);
}
} catch (Exception e) {
log.error("執(zhí)行定時(shí)任務(wù)失敗", e);
}
}
}5.使用

調(diào)用以上接口 ,參數(shù)如下。
"beanName":"任務(wù)類",
"methodName":"任務(wù)方法",
"params":"參數(shù)",
"cronExpression":"表達(dá)式",

總結(jié)
到此這篇關(guān)于可視化定時(shí)任務(wù)quartz集成解析的文章就介紹到這了,更多相關(guān)quartz集成解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 淺談SpringBoot集成Quartz動(dòng)態(tài)定時(shí)任務(wù)
- SpringBoot與Quartz集成實(shí)現(xiàn)分布式定時(shí)任務(wù)集群的代碼實(shí)例
- SpringBoot集成Quartz實(shí)現(xiàn)定時(shí)任務(wù)的方法
- Spring集成Quartz的簡單配置的方法
- 詳解Quartz 與 Spring框架集成的三種方式
- Spring Boot集成Quartz注入Spring管理的類的方法
- SpringBoot集成quartz實(shí)現(xiàn)定時(shí)任務(wù)詳解
- SpringBoot2.6.3集成quartz的方式
- Quartz與Spring集成的兩種方法示例
- springBoot項(xiàng)目集成quartz開發(fā)定時(shí)任務(wù)案例及注意事項(xiàng)
相關(guān)文章
java中Hutool工具類的常見使用場(chǎng)景詳解
在日常開發(fā)中,我們會(huì)使用很多工具類來提升項(xiàng)目開發(fā)的速度,而國內(nèi)用的比較多的 Hutool 框架,就是其中之一,本文我們就來介紹一下Hutool的具體使用吧2023-12-12
SPRINGMVC JSON數(shù)據(jù)交互如何實(shí)現(xiàn)
這篇文章主要介紹了SPRINGMVC JSON數(shù)據(jù)交互如何實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
SpringData JPA實(shí)現(xiàn)查詢分頁demo
本篇文章主要介紹了SpringData JPA實(shí)現(xiàn)查詢分頁demo,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03
Spring5.2.x 源碼本地環(huán)境搭建的方法步驟
這篇文章主要介紹了Spring5.2.x 源碼本地環(huán)境搭建的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Java如何基于IO流實(shí)現(xiàn)同一文件讀寫操作
這篇文章主要介紹了Java如何基于IO流實(shí)現(xiàn)文件讀寫操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10

