添加引用
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
QuartzFactory類:主要用於解決springboot quartz job 無法注入bean的問題。
package com.example.springboot; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; @Component public class QuartzFactory extends AdaptableJobFactory { /** * AutowireCapableBeanFactory接口是BeanFactory的子類 * 可以連接和填充那些生命週期不被Spring管理的已存在的bean實例 */ private AutowireCapableBeanFactory factory; public QuartzFactory(AutowireCapableBeanFactory factory) { this.factory = factory; } /** * 創建Job實例 */ @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 實例化對象 Object job = super.createJobInstance(bundle); // 進行注入(Spring管理該Bean) factory.autowireBean(job); //返回對象 return job; } }
QuartzConfig類:主要用於解決springboot quartz job 無法注入bean的問題,需要配合QuartzFactory類。
package com.example.springboot; import org.quartz.Scheduler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; @Configuration public class QuartzConfig { private QuartzFactory quartzFactory; public QuartzConfig(QuartzFactory quartzFactory) { this.quartzFactory = quartzFactory; } /** * 配置SchedulerFactoryBean * 將一個方法產生爲Bean並交給Spring容器管理 */ @Bean public SchedulerFactoryBean schedulerFactoryBean() { // Spring提供SchedulerFactoryBean爲Scheduler提供配置信息,並被Spring容器管理其生命週期 SchedulerFactoryBean factory = new SchedulerFactoryBean(); // 設置自定義Job Factory,用於Spring管理Job bean factory.setJobFactory(quartzFactory); return factory; } @Bean(name = "scheduler") public Scheduler scheduler() { return schedulerFactoryBean().getScheduler(); } }
QuartzService類:封裝一些任務調度的東西,主要也用於測試注入job的bean對象。
package com.example.springboot; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; import java.util.Date; @Slf4j @Service public class QuartzService { @Autowired private Scheduler scheduler; // 添加調度任務 public Boolean addScheduler(Integer id, String cron) throws Exception { // 判斷,如果因爲意外將過期調度任務傳遞過來,進行排除 // ... // 添加一個去重複的操作 if (scheduler.getJobDetail(JobKey.jobKey(id.toString())) != null) { return Boolean.TRUE; } // 構建一個job JobDetail jobDetail = JobBuilder .newJob(QuartzJob.class) .withIdentity(id.toString()) .usingJobData("param", id.toString()) .build(); // 構建一個trigger CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(id.toString()) .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); // 構建一個scheduler Date nextExecuteTime = scheduler.scheduleJob(jobDetail, trigger); //開始執行 scheduler.start(); return Boolean.TRUE; } // 取消調度任務,並且清除緩存 public Boolean deleteJob(Integer id) { Boolean result = Boolean.FALSE; try { // 刪除job result = scheduler.deleteJob(JobKey.jobKey(id.toString())); } catch (Exception ex) { // 記錄個日誌 log.error(String.format("%s-刪除調度器出現異常:%s,堆棧信息:%s", id, ex.getMessage(), ExceptionUtils.getStackTrace(ex))); } return result; } // 查詢調度任務 public Boolean query(Integer id) throws Exception { JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(id.toString())); if (jobDetail == null) { return Boolean.FALSE; } else { return Boolean.TRUE; } } // 得到cron的下一次執行時間 public String getNextExecuteTime(String cron) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 驗證cron的正確性 System.out.println(CronExpression.isValidExpression(cron)); if (CronExpression.isValidExpression(cron)) { CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("default") .withSchedule(CronScheduleBuilder.cronSchedule(cron)) .build(); // 獲取到下一次執行時間 Date nextTime = trigger.getFireTimeAfter(trigger.getStartTime()); return df.format(nextTime); } else { return "表達式不合法"; } } }
QuartzJob類:就是job要執行的類,該類如果不做特殊處理,是無法將bean對象注入該類的。重點就是QuartzFactory和QuartzConfig兩個類的添加,沒有什麼需要特別注意的東西。
package com.example.springboot; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Slf4j @Component public class QuartzJob implements Job { // 正常情況下,這裏是注入不進來的,故意寫個來檢驗注入bean問題 @Autowired private QuartzService quartzService; @Override public void execute(JobExecutionContext context) throws JobExecutionException { try { JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); // 獲取參數 String param = jobDataMap.getString("param"); System.out.println(param + "——開始執行"); Thread.sleep(1000); // 調用刪除調度器的方法 Boolean result = quartzService.deleteJob(Integer.valueOf(param)); System.out.println(param + "——執行結束,取消調度任務結果:" + result); } catch (Exception ex) { log.error(String.format("執行調度器出現異常:%s,堆棧信息:%s", ex.getMessage(), ExceptionUtils.getStackTrace(ex))); } } }
QuartzController類:測試類,用於調用任務的添加,刪除和查詢。
package com.example.springboot; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController public class QuartzController { @Autowired private QuartzService quartzService; @RequestMapping("/quartz/add/{id}") private Boolean index(@PathVariable Integer id, @RequestBody String cron) throws Exception { Boolean result = quartzService.addScheduler(id, cron); return result; } @RequestMapping("/quartz/delete/{id}") public Boolean cancel(@PathVariable Integer id) { Boolean result = quartzService.deleteJob(id); return result; } @RequestMapping("/quartz/query/{id}") public Boolean query(@PathVariable Integer id) throws Exception { Boolean result = quartzService.query(id); return result; } }
注意:由於job類無法注入spring bean對象,因此需要添加QuartzFactory和QuartzConfig兩個類,然後Scheduler調用方式,不是採用new,而是注入的方式,參見QuartzService中的Scheduler使用。
CronExpression cronExpression = new CronExpression("30 30 17 * * ? "); //這個方法沒有實現 System.out.println("getFinalFireTime - " + cronExpression.getFinalFireTime()); //得到給定時間下一個無效的時間 System.out.println("GetNextInvalidTimeAfter - " + cronExpression.getNextInvalidTimeAfter(new Date())); //得到給定時間的下一個有效的時間 System.out.println("GetNextValidTimeAfter - " + cronExpression.getNextValidTimeAfter(new Date())); //得到給定時間下一個符合表達式的時間 System.out.println("GetTimeAfter - " + cronExpression.getTimeAfter(new Date())); //這個方法沒有實現 System.out.println("GetTimeBefore - " + cronExpression.getTimeBefore(new Date())); //給定時間是否符合表達式 System.out.println("IsSatisfiedBy - " + cronExpression.isSatisfiedBy(new Date()));
吐槽一下,網上代碼都是各種copy,找點有用的找不到。就想找爲什麼job類無法注入,解決方案都找半天沒有用,後來自己總結搞了下,其實就是添加兩個類就可以了,很簡單,非得貼一大坨代碼,也沒有說明解釋。springboot quartz無法注入問題參見上述紅字把。