點擊上方 "程序員小樂"關注, 星標或置頂一起成長
後臺回覆“大禮包”有驚喜禮包!
關注訂閱號「程序員小樂」,收看更多精彩內容
每日英文
When you are free from desire, you will be happy, because you will never be disappointed.
當你沒有慾望,你就會快樂,因爲你永不會失望。
每日掏心話
懂得進退,才能成就人生;懂得取捨,才能淡定從容;懂得知足,才能怡養心性;懂得刪減,才能輕鬆釋然;懂得變通,纔會少走彎路;懂得反思,纔會提高自己。
來自:毅大師 | 責編:樂樂
鏈接:blog.csdn.net/qq_39648029/article/details/108993476
後端架構師(ID:study_tech)第 1068 次推文
往日回顧:京東單方面辭退38歲 P7 員工,勞動仲裁京東三次敗訴,員工復崗三天又收解聘通知
正文
看了好多文章,都只講了基礎的demo用法,也就是簡單的創建運行定時任務,對定時任務的管理卻很少。
我這裏從0開始搭建一個簡單的demo,包括定時任務的各種操作,以及API的一些用法,可以實現大多場景的需求。如:
-
普通定時任務的創建、啓動、停止。
-
動態創建定時任務,如創建一個訂單,5分鐘後執行某某操作。
一、整個 Quartz 的代碼流程基本基本如下:
-
首先需要創建我們的任務(Job),比如取消訂單、定時發送短信郵件之類的,這是我們的任務主體,也是寫業務邏輯的地方。
-
創建任務調度器(Scheduler),這是用來調度任務的,主要用於啓動、停止、暫停、恢復等操作,也就是那幾個api的用法。
-
創建任務明細(JobDetail),最開始我們編寫好任務(Job)後,只是寫好業務代碼,並沒有觸發,這裏需要用JobDetail來和之前創建的任務(Job)關聯起來,便於執行。
-
創建觸發器(Trigger),觸發器是來定義任務的規則的,比如幾點執行,幾點結束,幾分鐘執行一次等等。這裏觸發器主要有兩大類(SimpleTrigger和CronTrigger)。
-
根據Scheduler來啓動JobDetail與Trigger
二、進入正題,引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
三、創建Job
需實現Job接口,這個接口就一個execute()方法需要重寫,方法內容就是具體的業務邏輯。如果是動態任務呢,比如取消訂單,每次執行都是不同的訂單號。
這個時候就需要在創建任務(JobDetail)或者創建觸發器(Trigger)的那裏傳入參數,然後在這裏通過JobExecutionContext來獲取參數進行處理,
在公衆號程序員小樂後臺回覆“offer”,獲取算法面試題和答案。
import com.dy.utils.DateUtil;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
/**
* @program: xiudo-ota
* @description: 測試定時任務
* @author: zhang yi
* @create: 2020-10-09 14:38
*/
@DisallowConcurrentExecution//Job中的任務有可能併發執行,例如任務的執行時間過長,而每次觸發的時間間隔太短,則會導致任務會被併發執行。如果是併發執行,就需要一個數據庫鎖去避免一個數據被多次處理。
public class TestJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.err.println(jobExecutionContext.getJobDetail().getJobDataMap().get("name"));
System.err.println(jobExecutionContext.getJobDetail().getJobDataMap().get("age"));
System.err.println(jobExecutionContext.getTrigger().getJobDataMap().get("orderNo"));
System.err.println("定時任務執行,當前時間:"+ DateUtil.formatDateTime(new Date()));
}
}
四、創建任務調度器(Scheduler)
這裏採用Spring IOC,所以直接注入完事。如果是普通的,則需通過工廠創建。
工廠:
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
IOC:
@Autowired
private Scheduler scheduler;
五、創建任務明細(JobDetail)
/**通過JobBuilder.newJob()方法獲取到當前Job的具體實現(以下均爲鏈式調用)
* 這裏是固定Job創建,所以代碼寫死XXX.class
* 如果是動態的,根據不同的類來創建Job,則 ((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass()
* 即是 JobBuilder.newJob(((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass())
* */
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
/**給當前JobDetail添加參數,K V形式*/
.usingJobData("name","zy")
/**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getJobDetail().getJobDataMap().get("age")獲取值*/
.usingJobData("age",23)
/**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
.withIdentity("我是name","我是group")
.build();//執行
六、創建觸發器(Trigger)
這裏主要分爲兩大類SimpleTrigger、CronTrigger。
SimpleTrigger:是根據它自帶的api方法設置規則,比如每隔5秒執行一次、每隔1小時執行一次。
Trigger trigger = TriggerBuilder.newTrigger()
/**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
.usingJobData("orderNo", "123456")
/**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
.withIdentity("我是name","我是group")
/**立即生效*/
// .startNow()
/**開始執行時間*/
.startAt(start)
/**結束執行時間,不寫永久執行*/
.endAt(start)
/**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏*/
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
/**每隔3s執行一次,api方法有好多規則自行查看*/
.withIntervalInSeconds(3)
/**一直執行,如果不寫,定時任務就執行一次*/
.repeatForever()
)
.build();//執行
CronTrigger:這就比較常用了,是基於Cron表達式來實現的。
CronTrigger trigger = TriggerBuilder.newTrigger()
/**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
.usingJobData("orderNo", "123456")
/**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
.withIdentity("我是name","我是group")
/**立即生效*/
// .startNow()
/**開始執行時間*/
.startAt(start)
/**結束執行時間,不寫永久執行*/
.endAt(start)
/**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏,我這裏是demo,寫了個每2分鐘執行一次*/
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 * * * ?"))
.build();//執行
注意:.startNow( )和.startAt( )這裏有個坑,這兩個方法是對同一個成員變量進行修改的 也就是說startAt和startNow同時調用的時候任務開始的時間是按後面調用的方法爲主的,誰寫在後面用誰。
七、啓動任務
/**添加定時任務*/
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
/**啓動*/
scheduler.start();
}
以上,任務的創建啓動都完事了,後面就是任務的暫停、恢復、刪除。比較簡單,大致原理就是我們在創建任務明細(JobDetail)和創建觸發器(Trigger)時,會調用.withIdentity(key,group)來傳入認證信息,後續就是根據這些認證信息來管理任務(通過api方法)
八、任務的暫停
scheduler.pauseTrigger(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));
九、任務的恢復
scheduler.resumeTrigger(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));
根據你寫的方式來獲取。
在公衆號程序員小樂後臺回覆“Java”,獲取Java面試題和答案。
十、任務的刪除
scheduler.pauseTrigger(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));//暫停觸發器
scheduler.unscheduleJob(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));//移除觸發器
scheduler.deleteJob(JobKey.jobKey("我是剛纔寫的name","我是剛纔寫的group"));//刪除Job
最後附上基本代碼,Job實現在上面:
@Autowired
private Scheduler scheduler;
@PostMapping("/Quartz")
@ApiOperation(value = "定時任務_創建", notes = "創建")
@ResponseBody
public Object quartz(@RequestParam("orderNo") String orderNo) throws Exception {
Date start=new Date(System.currentTimeMillis() + 7 * 1000);//當前時間7秒之後
/**通過JobBuilder.newJob()方法獲取到當前Job的具體實現(以下均爲鏈式調用)
* 這裏是固定Job創建,所以代碼寫死XXX.class
* 如果是動態的,根據不同的類來創建Job,則 ((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass()
* 即是 JobBuilder.newJob(((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass())
* */
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
/**給當前JobDetail添加參數,K V形式*/
.usingJobData("name","zy")
/**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getJobDetail().getJobDataMap().get("age")獲取值*/
.usingJobData("age",23)
/**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
.withIdentity(orderNo)
.build();//執行
Trigger trigger = TriggerBuilder.newTrigger()
/**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
.usingJobData("orderNo", orderNo)
/**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
.withIdentity(orderNo)
/**立即生效*/
// .startNow()
/**開始執行時間*/
.startAt(start)
/**結束執行時間*/
// .endAt(start)
/**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏*/
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
/**每隔1s執行一次*/
.withIntervalInSeconds(3)
/**一直執行,*/
.repeatForever()
)
.build();//執行
//CronTrigger trigger = TriggerBuilder.newTrigger()
// /**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
// .usingJobData("orderNo", orderNo)
// /**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
// .withIdentity(orderNo)
// /**開始執行時間*/
// .startAt(start)
// /**結束執行時間*/
// .endAt(start)
// /**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏*/
// .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018"))
// .build();//執行
/**添加定時任務*/
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
/**啓動*/
scheduler.start();
}
System.err.println("--------定時任務啓動成功 "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+" ------------");
return "ok";
}
@PostMapping("/shutdown")
@ApiOperation(value = "定時任務_停止", notes = "停止")
@ResponseBody
public Object shutdown(@RequestParam("orderNo") String orderNo) throws IOException, SchedulerException {
scheduler.pauseTrigger(TriggerKey.triggerKey(orderNo));//暫停Trigger
return "";
}
@PostMapping("/resume")
@ApiOperation(value = "定時任務_恢復", notes = "恢復")
@ResponseBody
public Object resume(@RequestParam("orderNo") String orderNo) throws IOException, SchedulerException {
scheduler.resumeTrigger(TriggerKey.triggerKey(orderNo));//恢復Trigger
return "ok";
}
@PostMapping("/del")
@ApiOperation(value = "定時任務_刪除", notes = "刪除")
@ResponseBody
public Object del(@RequestParam("orderNo") String orderNo) throws IOException, SchedulerException {
scheduler.pauseTrigger(TriggerKey.triggerKey(orderNo));//暫停觸發器
scheduler.unscheduleJob(TriggerKey.triggerKey(orderNo));//移除觸發器
scheduler.deleteJob(JobKey.jobKey(orderNo));//刪除Job
return "ok";
}
完事。。。。。。,如果想讓定時任務在啓動項目後自動啓動,則需要持久化任務,可以把基本信息保存在數據庫,項目啓動時啓動完,或者做分佈式任務。
PS:歡迎在留言區留 下你的觀點,一起討論提高。如果今天的文章讓你有新的啓發,歡迎轉發分享給更多人。
猜你還想看
任正非在榮耀送別會上的講話:一旦“離婚”就不要藕斷絲連,要做華爲全球最強的競爭對手
嘿,你在看嗎?