定时任务 Scheduled quartz
在項目應用中往往會用到任務定時器的功能,比如某某時間,或者多少多少秒然后執行某個騷操作等。
spring 支持多種定時任務的實現,其中不乏自身提供的定時器。
接下來介紹一下使用 spring 的定時器和使用 quartz 定時器。
前言
spring 自身提供了定時任務,為什么還要使用 quartz 呢?
使用 spring 自帶的定時任務可以很簡單很方便的完成一些簡單的定時任務,沒錯,這里提到的是簡單,因此我們想動態的執行我們的定時任務是非常困難的。然而使用 quartz 卻可以很容易的管理我們的定時任務,很容易動態的操作定時任務。
1、 使用spring的定時器
spring 自帶支持定時器的任務實現,其可通過簡單配置來使用到簡單的定時任務。
@Component @Configurable @EnableScheduling public class ScheduledTasks{/*** 方式一* 每6秒執行一次**/@Scheduled(fixedRate = 6000)public void reportCurrentByCron(){System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ()));}/*** 方式二* 每6秒執行一次**/@Scheduled(cron = "*/6 * * * * * ")public void reportCurrentByCron(){System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ()));}}參數說明
@Scheduled 參數可以接受兩種定時的設置,一種是我們常用的 cron="/6?* * * ?",一種是 fixedRate = 6000,兩種都可表示固定周期執行定時任務。
fixedRate說明
- @Scheduled(fixedRate = 6000):上一次開始執行時間點之后 6 秒再執行。
- @Scheduled(fixedDelay = 6000):上一次執行完畢時間點之后 6 秒再執行。
- @Scheduled(initialDelay=1000, fixedRate=6000):第一次延遲 1 秒后執行,之后按 fixedRate 的規則每 6 秒執行一次。
cron說明
cron 一定有七位數,最后一位是年,SpringBoot 定時方案只需要設置六位即可:
- 第一位, 表示秒, 取值是0 ~ 59
- 第二位, 表示分. 取值是0 ~ 59
- 第三位, 表示小時, 取值是0 ~ 23
- 第四位, 表示天/日, 取值是0 ~ 31
- 第五位, 表示月份, 取值是1 ~ 12
- 第六位, 表示星期, 取值是1 ~ 7, 星期一,星期二…, 還有 1 表示星期日
- 第七位, 年份, 可以留空, 取值是1970 ~ 2099
cron中,還有一些特殊的符號,含義如下:
- (*) 星號,可以理解為每的意思,每秒、每分、每天、每月、每年…。
- (?) 問號,問號只能出現在日期和星期這兩個位置,表示這個位置的值不確定,每天 3 點執行,因此第六位星期的位置,是不需要關注的,就是不確定的值;同時,日期和星期是兩個相互排斥的元素,通過問號來表明不指定值,比如 1 月 10 日是星期一,如果在星期的位置另指定星期二,就前后沖突矛盾了。
- (-) 減號,表達一個范圍,如在小時字段中使用“10 - 12”,則表示從 10 到 12 點,即 10、11、12。
- (,) 逗號,表達一個列表值,如在星期字段中使用“1,2,4”,則表示星期一、星期二、星期四。
- (/) 斜杠,如 x/y,x 是開始值,y 是步長,比如在第一位(秒),0/15 就是從 0 秒開始,每隔 15 秒執行一次,最后就是 0、15、30、45、60,另 */y,等同于 0/y。
舉幾個例子熟悉一下:
- 0 0 3 * * ? :每天 3 點執行;
- 0 5 3 * * ? :每天 3 點 5 分執行;
- 0 5 3 ? * * :每天 3 點 5 分執行,與上面作用相同;
- 0 5/10 3 * * ?:每天 3 點的 5 分、15 分、25 分、35 分、45 分、55分這幾個時間點執行;
- 0 10 3 ? * 1:每周星期天,3 點 10 分執行,注,1 表示星期天;
- 0 10 3 ? * 1#3:每個月的第三個星期,星期天執行,# 號只能出現在星期的位置。
ok,spring的定時器就像如上這么簡單,涉及到的幾個注解:
@EnableScheduling:標注啟動定時任務。?????????
@Scheduled:? 定義某個定時任務。
2、使用quartz實現定時任務
quartz 的設計者做了一個設計選擇來從調度分離開作業。
quartz 中的觸發器用來告訴調度程序作業什么時候觸發,框架提供了一把觸發器類型,但兩個最常用的是 SimpleTrigger 和 CronTrigger。
SimpleTrigger 為需要簡單打火調度而設計。典型地,如果你需要在給定的時間和重復次數或者兩次打火之間等待的秒數打火一個作業,那么SimpleTrigger適合你。
另一方面,如果你有許多復雜的作業調度,那么或許需要CronTrigger。
什么是復雜調度?
當你需要在除星期六和星期天外的每天上午10點半執行作業時,那么應該使用CronTrigger。正如它的名字所暗示的那樣,CronTrigger是基于Unix克隆表達式的。
開始之前需要了解的幾個概念:
- Job:是一個接口,只定義一個方法 execute(JobExecutionContext context),在實現接口的 execute 方法中編寫所需要定時執行的 Job(任務),JobExecutionContext 類提供了調度應用的一些信息;Job 運行時的信息保存在 JobDataMap 實例中。
- JobDetail:Quartz 每次調度 Job 時,都重新創建一個 Job 實例,因此它不接受一個 Job 的實例,相反它接收一個 Job 實現類(JobDetail,描述 Job 的實現類及其他相關的靜態信息,如 Job 名字、描述、關聯監聽器等信息),以便運行時通過 newInstance() 的反射機制實例化 Job。
- Trigger:是一個類,描述觸發 Job 執行的時間觸發規則,主要有 SimpleTrigger 和 CronTrigger 這兩個子類,上邊剛剛有提到。當且僅當需調度一次或者以固定時間間隔周期執行調度,SimpleTrigger 是最適合的選擇;而 CronTrigger 則可以通過 Cron 表達式定義出各種復雜時間規則的調度方案:如工作日周一到周五的 15:00 ~ 16:00 執行調度等。
- Scheduler:調度器就相當于一個容器,裝載著任務和觸發器,該類是一個接口,代表一個 Quartz 的獨立運行容器,Trigger 和 JobDetail 可以注冊到 Scheduler 中,兩者在 Scheduler 中擁有各自的組及名稱,組及名稱是 Scheduler 查找定位容器中某一對象的依據,Trigger 的組及名稱必須唯一,JobDetail 的組和名稱也必須唯一(但可以和 Trigger 的組和名稱相同,因為它們是不同類型的)。Scheduler 定義了多個接口方法,允許外部通過組及名稱訪問和控制容器中 Trigger 和 JobDetail。
上邊的四個概念,建議通讀一遍,結合下方代碼,思路更清晰。
SpringBoot2.x 之后,完成了對 Quartz 自動化配置集成,省去了很多繁瑣的配置,下面進入正題吧。
2.1、引入依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId> </dependency>2.2、yml配置
spring:quartz:# 任務信息存儲至?MEMORY(內存方式:默認)、JDBC(數據庫方式)job-store-type: jdbcproperties:org:quartz:jobStore:misfireThreshold: 1002.3、用于增刪改查定時任務的Controller
/*** @author niceyoo*/ @Slf4j @RestController @Api(description = "定時任務管理接口") @RequestMapping("/tmax/quartzJob") public class TmaxQuartzJobController {/*** 定時任務service*/@Autowiredprivate QuartzJobService quartzJobService;/*** 調度器*/@Autowiredprivate Scheduler scheduler;@RequestMapping(value = "/add", method = RequestMethod.POST)@ApiOperation(value = "添加定時任務")public Result<Object> addJob(@ModelAttribute QuartzJob job){add(job.getJobClassName(),job.getCronExpression(),job.getParameter());quartzJobService.save(job);return new ResultUtil<Object>().setSuccessMsg("創建定時任務成功");}@RequestMapping(value = "/edit", method = RequestMethod.POST)@ApiOperation(value = "更新定時任務")public Result<Object> editJob(@ModelAttribute QuartzJob job){delete(job.getJobClassName());add(job.getJobClassName(),job.getCronExpression(),job.getParameter());job.setStatus(CommonConstant.STATUS_NORMAL);quartzJobService.update(job);return new ResultUtil<Object>().setSuccessMsg("更新定時任務成功");}@RequestMapping(value = "/pause", method = RequestMethod.POST)@ApiOperation(value = "暫停定時任務")public Result<Object> pauseJob(@ModelAttribute QuartzJob job){try {scheduler.pauseJob(JobKey.jobKey(job.getJobClassName()));} catch (SchedulerException e) {throw new TmaxException("暫停定時任務失敗");}job.setStatus(CommonConstant.STATUS_DISABLE);quartzJobService.update(job);return new ResultUtil<Object>().setSuccessMsg("暫停定時任務成功");}@RequestMapping(value = "/resume", method = RequestMethod.POST)@ApiOperation(value = "恢復定時任務")public Result<Object> resumeJob(@ModelAttribute QuartzJob job){try {scheduler.resumeJob(JobKey.jobKey(job.getJobClassName()));} catch (SchedulerException e) {throw new TmaxException("恢復定時任務失敗");}job.setStatus(CommonConstant.STATUS_NORMAL);quartzJobService.update(job);return new ResultUtil<Object>().setSuccessMsg("恢復定時任務成功");}@RequestMapping(value = "/delByIds/{ids}", method = RequestMethod.DELETE)@ApiOperation(value = "刪除定時任務")public Result<Object> deleteJob(@PathVariable String[] ids){for(String id:ids){QuartzJob job = quartzJobService.get(id);delete(job.getJobClassName());quartzJobService.delete(job);}return new ResultUtil<Object>().setSuccessMsg("刪除定時任務成功");}/*** 添加定時任務* @param jobClassName* @param cronExpression* @param parameter*/public void add(String jobClassName, String cronExpression, String parameter){try {//啟動調度器scheduler.start();//構建job信息JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName).usingJobData("parameter", parameter).build();//表達式調度構建器(即任務執行的時間) 使用withMisfireHandlingInstructionDoNothing() 忽略掉調度暫停過程中沒有執行的調度CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();//按新的cronExpression表達式構建一個新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName).withSchedule(scheduleBuilder).build();scheduler.scheduleJob(jobDetail, trigger);} catch (SchedulerException e) {log.error(e.toString());throw new TmaxException("創建定時任務失敗");} catch (Exception e){throw new TmaxException("后臺找不到該類名任務");}}/*** 刪除定時任務* @param jobClassName*/public void delete(String jobClassName){try {scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName));scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName));scheduler.deleteJob(JobKey.jobKey(jobClassName));} catch (Exception e) {throw new TmaxException("刪除定時任務失敗");}}public static Job getClass(String classname) throws Exception {Class<?> class1 = Class.forName(classname);return (Job)class1.newInstance();}}2.4、Job執行的任務類
@Slf4j public class SampleJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {log.info(String.format("打印時間:"+ DateUtil.now()));} }4小步代碼看完后,我們再來分析一波 quartz 的4個核心概念,先腦補一張圖。
主要看 TmaxQuartzJobController 的 add() 方法
- Job 為作業的接口,為任務調度的對象,在代碼中體現為 <SampleJob.java>,代碼中是由用戶傳遞過來的類絕對路徑;
- JobDetail 用來描述 Job 的實現類及其他相關的靜態信息,可以看到代碼中通過 JobBuilder 的靜態方法 newJob(Class jobClass) 生成 JobBuilder 實例,其中 jobClass 的獲取采用反射機制;
- Trigger 做為作業的定時管理工具,一個 Trigger 只能對應一個作業實例,而一個作業實例可對應多個觸發器,代碼中采用的是 CronTrigger,通過表達式調度構建器構建任務的執行時間,注意,任務的執行時間是由前臺傳遞過來的 cron 表達式,然后按新的 cronExpression 表達式構建一個新的 trigger,TriggerBuilder.newTrigger().withIdentity(jobClassName) 表達了一個 Trigger 只能對應一個作業實例;
- Scheduler 做為定時任務容器,是 Quartz 最上層的東西,它提攜了所有觸發器和作業,使它們協調工作,每個 Scheduler 都存有 JobDetail 和 Trigger 的注冊,一個 Scheduler 中可以注冊多個 JobDetail 和多個 Trigger。
最后,調用 Controller 層的任務添加方法 /add 完成全部,效果如下:
2019-05-23 00:38:38.455 INFO 19856 --- [eduler_Worker-1] club.sscai.tmax.quartz.jobs.SampleJob : 打印時間:2019-05-24 09:38:38 2019-05-23 00:38:39.035 INFO 19856 --- [eduler_Worker-2] club.sscai.tmax.quartz.jobs.SampleJob : 打印時間:2019-05-24 09:38:39 2019-05-23 00:38:40.752 INFO 19856 --- [eduler_Worker-3] club.sscai.tmax.quartz.jobs.SampleJob : 打印時間:2019-05-24 09:38:40 2019-05-23 00:38:41.033 INFO 19856 --- [eduler_Worker-4] club.sscai.tmax.quartz.jobs.SampleJob : 打印時間:2019-05-24 09:38:41 2019-05-23 00:38:42.640 INFO 19856 --- [eduler_Worker-5] club.sscai.tmax.quartz.jobs.SampleJob : 打印時間:2019-05-24 09:38:42 2019-05-23 00:38:43.023 INFO 19856 --- [eduler_Worker-6] club.sscai.tmax.quartz.jobs.SampleJob : 打印時間:2019-05-24 09:38:43習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:niceyoo
總結
以上是生活随笔為你收集整理的定时任务 Scheduled quartz的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【JZOJ3598】【CQOI2014】
- 下一篇: 极域电子教室V4窗口化