javascript
Springboot2 Quartz实现JAVA定时任务的动态配置
動(dòng)態(tài)配置Quartz。沒(méi)接觸過(guò)定時(shí)任務(wù)的同學(xué)可以先看下此篇:JAVA定時(shí)任務(wù)實(shí)現(xiàn)的幾種方式
文章目錄
- 一、需求背景
- 1. 問(wèn)題現(xiàn)象
- 2. 問(wèn)題分析
- 3. 解決方案
- 二、需求背景
- 2.1. maven依賴(lài)
- 2.2. 創(chuàng)建一個(gè)任務(wù)表
- 2.3. 實(shí)現(xiàn)步驟
- 三、代碼邏輯
- 3.1. Quartz配置
- 3.2. 啟動(dòng)項(xiàng)目,加載監(jiān)聽(tīng)
- 3.3. 讀取數(shù)據(jù)庫(kù),加載scheduler調(diào)度器
- 3.4. 添加任務(wù)到Quartz調(diào)度器
- 3.5. 根據(jù)任務(wù)調(diào)度運(yùn)行job類(lèi)
- 3.6. 實(shí)例化job類(lèi),注入要運(yùn)行的service
- 四、常見(jiàn)的Quartz的API
- 4.1. 新增一個(gè)job
- 4.2. 暫停一個(gè)job
- 4.2. 刪除一個(gè)job
- 4.3. 暫停一個(gè)job
- 4.4. 立即執(zhí)行一個(gè)job
- 4.5. 更新job表達(dá)式
- 五、源碼下載和運(yùn)行
- 5.1. 源碼地址
- 5.2. 源碼下載
- 5.3. 項(xiàng)目運(yùn)行
一、需求背景
1. 問(wèn)題現(xiàn)象
定時(shí)任務(wù)實(shí)現(xiàn)方式: 1.JDK 的Timer類(lèi) 2.Quartz 3.SpringTask 。
生產(chǎn)上三種方式我都有使用過(guò)。但是使用過(guò)程中用的最多的便是xml配置的方式,這種方式最簡(jiǎn)單,無(wú)代碼侵入,也比較好理解。但是卻有個(gè)致命的缺點(diǎn),比如你要改某個(gè)任務(wù)的觸發(fā)時(shí)間,亦或是你要新增一個(gè)任務(wù),暫停一個(gè)任務(wù)。怎么做?
停應(yīng)用!改XML配置!重新啟動(dòng)!
怎樣解決上述致命的問(wèn)題現(xiàn)象呢?
2. 問(wèn)題分析
請(qǐng)大家想一想:停應(yīng)用、修改XML配置!重新啟動(dòng)是為了什么?
應(yīng)該是首先修改Quartz的配置文件,然后,修改一些屬性配置參數(shù),最后重新啟動(dòng)項(xiàng)目加載配置文件,對(duì)吧!
哪怎樣可以動(dòng)態(tài)配置Quartz的配置文件的呢?
3. 解決方案
哪怎樣動(dòng)態(tài)配置Quartz的屬性參數(shù)的呢?如果把Quartz需要的重點(diǎn)關(guān)鍵參數(shù)存到數(shù)據(jù)庫(kù),通過(guò)頁(yè)面就可以修改Quartz的屬性參數(shù)了,對(duì)吧!
那問(wèn)題又來(lái)了,屬性參數(shù)修改了,那有怎樣讓屬性參數(shù)生效呢?
其實(shí)Quartz官方提供了一些日常操作的API接口方法,只需要咱們把需要的參數(shù)傳遞給對(duì)應(yīng)的api接口即可,因此,咱們的思路,應(yīng)該這樣想,操作增增加定時(shí)任務(wù)就找Quartz的增加api,傳參數(shù),其他的一樣,對(duì)吧!
既然思路有了,那咱們就應(yīng)該提前去做以下幾件事:
1.Quartz常見(jiàn)的api在哪?或者說(shuō)在哪里可以找到
2.找出常見(jiàn)的Quartz的api
3.認(rèn)真分析常見(jiàn)的Quartz的api需要怎樣才能觸發(fā)
溫馨建議:
自己寫(xiě)一個(gè)demo或者main方法單元測(cè)試一下,效果最好,不斷完善
舉個(gè)例子:
增加定時(shí)任務(wù),一般需要幾個(gè)或者什么參數(shù),提前弄明白
這樣就可以動(dòng)態(tài)不停應(yīng)用通過(guò)Quartz實(shí)現(xiàn)了不停機(jī)添加、暫停、刪除、立即觸發(fā)任務(wù)的方法
二、需求背景
2.1. maven依賴(lài)
<!-- quartz --> <dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.1</version> </dependency> <dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId> </dependency>2.2. 創(chuàng)建一個(gè)任務(wù)表
CREATE TABLE `sys_task` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`job_name` varchar(255) DEFAULT NULL COMMENT '任務(wù)名',`description` varchar(255) DEFAULT NULL COMMENT '任務(wù)描述',`cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron表達(dá)式',`bean_class` varchar(255) DEFAULT NULL COMMENT '任務(wù)執(zhí)行時(shí)調(diào)用哪個(gè)類(lèi)的方法 包名+類(lèi)名',`job_status` varchar(255) DEFAULT NULL COMMENT '任務(wù)狀態(tài)',`job_group` varchar(255) DEFAULT NULL COMMENT '任務(wù)分組',`create_user` varchar(64) DEFAULT NULL COMMENT '創(chuàng)建者',`create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',`update_user` varchar(64) DEFAULT NULL COMMENT '更新者',`update_time` datetime DEFAULT NULL COMMENT '更新時(shí)間',PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;2.3. 實(shí)現(xiàn)步驟
①啟動(dòng)項(xiàng)目,啟動(dòng)task監(jiān)聽(tīng)
②讀取數(shù)據(jù)庫(kù),將開(kāi)啟的任務(wù)job和trigger加載到scheduler調(diào)度器
③根據(jù)任務(wù)調(diào)度運(yùn)行job類(lèi)
④每次運(yùn)行利用AdaptableJobFactory實(shí)例化job類(lèi),以便注入要運(yùn)行的service
聽(tīng)著是不是很簡(jiǎn)單,但卻還是一頭霧水,且聽(tīng)我慢慢道來(lái)~~
三、代碼邏輯
3.1. Quartz配置
Springboot的配置方法,常規(guī)Spring項(xiàng)目可以在xml中配置
@Configuration public class QuartzConfigration {@Autowiredprivate JobFactory jobFactory;@Beanpublic SchedulerFactoryBean schedulerFactoryBean() {SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();try {schedulerFactoryBean.setOverwriteExistingJobs(true);schedulerFactoryBean.setQuartzProperties(quartzProperties());schedulerFactoryBean.setJobFactory(jobFactory);} catch (Exception e) {e.printStackTrace();}return schedulerFactoryBean;}// 指定quartz.properties,可在配置文件中配置相關(guān)屬性@Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/config/quartz.properties"));propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}// 創(chuàng)建schedule@Bean(name = "scheduler")public Scheduler scheduler() {return schedulerFactoryBean().getScheduler();} }3.2. 啟動(dòng)項(xiàng)目,加載監(jiān)聽(tīng)
@Component @Order(value = 1) public class ScheduleJobInitListener implements CommandLineRunner {@AutowiredTaskService scheduleJobService;@Overridepublic void run(String... arg0) throws Exception {try {scheduleJobService.initSchedule();} catch (Exception e) {e.printStackTrace();}} }CommandLineRunner類(lèi)似Spring框架的ApplicationListener監(jiān)聽(tīng)器。官方的解釋是:
Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or Order @Order annotation.
接口被用作將其加入spring容器中時(shí)執(zhí)行其run方法。多個(gè)CommandLineRunner可以被同時(shí)執(zhí)行在同一個(gè)spring上下文中并且執(zhí)行順序是以order注解的參數(shù)順序一致。
3.3. 讀取數(shù)據(jù)庫(kù),加載scheduler調(diào)度器
@Overridepublic void initSchedule() throws SchedulerException {// 這里獲取任務(wù)信息數(shù)據(jù)List<TaskDO> jobList = taskMapper.list();for (TaskDO task : jobList) {if (JobStatusEnum.RUNNING.getCode().equals(task.getJobStatus())) {quartzManager.addJob(task);}}}3.4. 添加任務(wù)到Quartz調(diào)度器
/*** 添加任務(wù)*/ @SuppressWarnings("unchecked")public void addJob(TaskDO task) {try {// 創(chuàng)建jobDetail實(shí)例,綁定Job實(shí)現(xiàn)類(lèi)// 指明job的名稱(chēng),所在組的名稱(chēng),以及綁定job類(lèi)Class<? extends Job> jobClass = (Class<? extends Job>) (Class.forName(task.getBeanClass()).newInstance().getClass());JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(task.getJobName(), task.getJobGroup())// 任務(wù)名稱(chēng)和組構(gòu)成任務(wù)key.build();// 定義調(diào)度觸發(fā)規(guī)則// 使用cornTrigger規(guī)則Trigger trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobName(), task.getJobGroup())// 觸發(fā)器key.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND)).withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression())).startNow().build();// 把作業(yè)和觸發(fā)器注冊(cè)到任務(wù)調(diào)度中scheduler.scheduleJob(jobDetail, trigger);// 啟動(dòng)if (!scheduler.isShutdown()) {scheduler.start();}} catch (Exception e) {e.printStackTrace();}}Scheduler作為Quartz的核心調(diào)度器,有將近50多個(gè)API接口,包括任務(wù)的添加,暫停,恢復(fù),刪除等一系列的API,這里僅介紹一些常用的,想要了解更多可以稍后看下彩蛋部分。
1、start()方法:只有調(diào)用start()方法后,Scheduler線程才開(kāi)始啟動(dòng)觸發(fā)器trigger,運(yùn)行job
2、pauseJob(JobKey jobKey) :根據(jù)指定的JobDetail key暫停job。
3、resumeJob(JobKey jobKey) :根據(jù)指定的key恢復(fù)一個(gè)job。
4、deleteJob(JobKey jobKey) :刪除一個(gè)job
5、triggerJob(JobKey jobKey) :觸發(fā)一個(gè)JobDetail(現(xiàn)在執(zhí)行)。
6、rescheduleJob(TriggerKey triggerKey, Trigger newTrigger):
用給定的鍵刪除觸發(fā)器,并存儲(chǔ)新的觸發(fā)器,它必須與同一個(gè)作業(yè)相關(guān)聯(lián)(新觸發(fā)器必須具有指定的作業(yè)名和組)-然而,新觸發(fā)器不必具有與舊觸發(fā)器相同的名稱(chēng)。
3.5. 根據(jù)任務(wù)調(diào)度運(yùn)行job類(lèi)
其實(shí)這一步是不需要我們編寫(xiě)的,在我們將正確的JobDetail 和 Trigger 表達(dá)式加載到任務(wù)調(diào)度后,調(diào)度器會(huì)自動(dòng)觸發(fā)任務(wù)的執(zhí)行
3.6. 實(shí)例化job類(lèi),注入要運(yùn)行的service
工廠類(lèi)
@Component public class JobFactory extends AdaptableJobFactory {//這個(gè)對(duì)象Spring會(huì)幫我們自動(dòng)注入進(jìn)來(lái),也屬于Spring技術(shù)范疇.//為什么需要這個(gè)類(lèi)呢,在我寫(xiě)的這個(gè)demo中,大家可以將此類(lèi)刪掉,發(fā)現(xiàn)程序也可以正確運(yùn)行,可是我為什么還是加上呢。//大家可以看下我們的任務(wù)類(lèi),大家可以看到Job對(duì)象的實(shí)例化過(guò)程是在Quartz中進(jìn)行的,這時(shí)候我們將spring的東西注入進(jìn)來(lái),肯定是行不通的,所以需要這個(gè)類(lèi)@Autowiredprivate AutowireCapableBeanFactory capableBeanFactory;@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {//調(diào)用父類(lèi)的方法Object jobInstance = super.createJobInstance(bundle);//進(jìn)行注入capableBeanFactory.autowireBean(jobInstance);return jobInstance;} }任務(wù)類(lèi)
@DisallowConcurrentExecution //作業(yè)不并發(fā) @Component public class HelloWorldJob implements Job{@Overridepublic void execute(JobExecutionContext arg0) throws JobExecutionException {System.out.println("歡迎使用yyblog,這是一個(gè)定時(shí)任務(wù) --小賣(mài)鋪的老爺爺!"+ DateUtils.fullTime(new Date())); } }好了,大功告成,一個(gè)簡(jiǎn)單的動(dòng)態(tài)配置的定時(shí)任務(wù)已經(jīng)完成。是不是so easy!
下面我們?cè)賮?lái)簡(jiǎn)單實(shí)現(xiàn)下其他的幾種常用的api吧。
四、常見(jiàn)的Quartz的API
4.1. 新增一個(gè)job
/*** 添加任務(wù)* * @param scheduleJob* @throws SchedulerException*/ @SuppressWarnings("unchecked")public void addJob(TaskDO task) {try {// 創(chuàng)建jobDetail實(shí)例,綁定Job實(shí)現(xiàn)類(lèi)// 指明job的名稱(chēng),所在組的名稱(chēng),以及綁定job類(lèi)Class<? extends Job> jobClass = (Class<? extends Job>) (Class.forName(task.getBeanClass()).newInstance().getClass());JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(task.getJobName(), task.getJobGroup())// 任務(wù)名稱(chēng)和組構(gòu)成任務(wù)key.build();// 定義調(diào)度觸發(fā)規(guī)則// 使用cornTrigger規(guī)則Trigger trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobName(), task.getJobGroup())// 觸發(fā)器key.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND)).withSchedule(CronScheduleBuilder.cronSchedule(task.getCronExpression())).startNow().build();// 把作業(yè)和觸發(fā)器注冊(cè)到任務(wù)調(diào)度中scheduler.scheduleJob(jobDetail, trigger);// 啟動(dòng)if (!scheduler.isShutdown()) {scheduler.start();}} catch (Exception e) {e.printStackTrace();}}4.2. 暫停一個(gè)job
/*** 暫停一個(gè)job* * @param task* @throws SchedulerException*/public void pauseJob(TaskDO task) throws SchedulerException {JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());scheduler.pauseJob(jobKey);}4.2. 刪除一個(gè)job
/*** 刪除一個(gè)job* * @param task* @throws SchedulerException*/public void deleteJob(TaskDO task) throws SchedulerException {JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());scheduler.deleteJob(jobKey);}4.3. 暫停一個(gè)job
在這里插入代碼片4.4. 立即執(zhí)行一個(gè)job
/*** 立即執(zhí)行job* * @param task* @throws SchedulerException*/public void runJobNow(TaskDO task) throws SchedulerException {JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());scheduler.triggerJob(jobKey);}4.5. 更新job表達(dá)式
/*** 更新job時(shí)間表達(dá)式* * @param task* @throws SchedulerException*/public void updateJobCron(TaskDO task) throws SchedulerException {TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobName(), task.getJobGroup());CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCronExpression());trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();scheduler.rescheduleJob(triggerKey, trigger);}五、源碼下載和運(yùn)行
5.1. 源碼地址
https://gitee.com/gb_90/yyblog
5.2. 源碼下載
git clone git@gitee.com:gb_90/yyblog.git5.3. 項(xiàng)目運(yùn)行
#1.進(jìn)入下載的項(xiàng)目目錄 cd yyblog #2.切換分支到1.0 git checkout 1.0 #3.初始化數(shù)據(jù)庫(kù)(sql在lib下面) #4.修改數(shù)據(jù)庫(kù)連接(application.yml) #5.啟動(dòng)項(xiàng)目 鏈接:http://localhost:8080 賬號(hào):admin 密碼:123456Quartz文檔地址:https://github.com/allanzhuo/yyblog/tree/master/doc
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的Springboot2 Quartz实现JAVA定时任务的动态配置的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Ant Design Vue 表格数据按
- 下一篇: Flowable 数据库表结构 ACT_