日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Quartz详解和使用CommandLineRunner在项目启动时初始化定时任务

發布時間:2024/9/30 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Quartz详解和使用CommandLineRunner在项目启动时初始化定时任务 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • Quartz介紹
  • 自定義CommandLineRunner類:
  • 創建、更新定時任務
  • service層
  • 自定義QuartzJobBean
  • 智能調度組件
  • 定時任務實體類:
  • mapper接口:
  • 時間觸發器的一些寫法

之前與定時任務相關的一個文章記錄:
springboot使用@Scheduled作定時任務詳細用法

Quartz介紹

Quartz是一個完全由java編寫的功能豐富的開源作業調度庫,可以集成到幾乎任何Java應用程序中,小到獨立應用程序,大到大型的電子商務系統。Quartz可以用來創建執行數十,數百乃至數萬個作業的簡單或復雜的計劃;作業的任務被定義為標準的Java組件,它可以執行幾乎任何你可能編程的任務。而且Quartz Scheduler包含許多企業級功能,例如支持JTA事務和集群。

了解一下Quartz中涉及到的幾個類概念:

  • SchedulerFactory:調度器工廠。這是一個接口,用于調度器的創建和管理。示例中使用的是Quartz中的默認實現。

  • Scheduler:任務調度器。它表示一個Quartz的獨立運行容器,里面注冊了多個觸發器(Trigger)和任務實例(JobDetail)。兩者分別通過各自的組(group)和名稱(name)作為容器中定位某一對象的唯一依據,因此組和名稱必須唯一(觸發器和任務實例的組和名稱可以相同,因為對象類型不同)。

  • Job:是一個接口,只有一個方法void execute(JobExecutionContext context),開發者實現該接口定義運行任務,JobExecutionContext類提供了調度上下文的各種信息。Job運行時的信息保存在JobDataMap實例中。

  • JobDetail:Job實例。Quartz在每次執行Job時,都重新創建一個Job實例,所以它不直接接受一個Job的實例,相反它接收一個Job實現類,以便運行時通過newInstance()的反射機制實例化Job。因此需要通過一個類來描述Job的實現類及其它相關的靜態信息,如Job名字、描述、關聯監聽器等信息,JobDetail承擔了這一角色。

  • Trigger

    :觸發器,描述觸發Job執行的時間觸發規則。

    • SimpleTrigger:當僅需觸發一次或者以固定時間間隔周期執行,SimpleTrigger是最適合的選擇。
    • CronTrigger:通過Cron表達式定義出各種復雜時間規則的調度方案:如每早晨9:00執行,周一、周三、周五下午5:00執行等。
      整個Quartz運行調度的流程如下:
  • 通過觸發器工廠(SchedulerFactory的實現類)創建一個調度器(Scheduler);
  • 創建一個任務實例(JobDetail),為它指定實現了Job接口的實現類(示例中的HelloWord.class),并指定唯一標識(Identity)組(示例中的“group1”)和名稱(示例中的“myJob”);
  • 創建一個觸發器(Trigger),為它指定時間觸發規則(示例中的simpleSchedule()生成的SimpleTrigger),并指定唯一標識(Identity)組(示例中的“group1”)和名稱(示例中的“myTrigger”)
  • 最后通過調度器(Scheduler)將任務實例(JobDetail)和觸發器(Trigger)綁定在一起,并通過start()方法開啟任務調度。
  • 自定義CommandLineRunner類:

    關于CommandLineRunner:平常開發中有可能需要實現在項目啟動后執行的功能,SpringBoot提供的一種簡單的實現方案就是添加一個model并實現CommandLineRunner接口,實現功能的代碼放在實現的run方法中
    這個類的run方法String… args是應用啟動的時候可以傳進來的參數,有兩種方式可以傳參

    一種是命令行的方式傳參,所以這個接口叫CommandLineRunner
    另一種方法是通過IntelliJ IDEA配置參數
    命令行傳參
    首先將應用打成jar包,然后運行如下命令行,這里傳入三個參數

    java -jar MyProject.jar my name is
    或者在idea中配置:

    代碼:

    import com.itheima.pinda.entity.ScheduleJobEntity; import com.itheima.pinda.mapper.ScheduleJobMapper; import com.itheima.pinda.utils.ScheduleUtils; import lombok.extern.slf4j.Slf4j; import org.quartz.CronTrigger; import org.quartz.Scheduler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component;import java.util.List;/*** 在項目啟動時初始化定時任務*/ @Component @Slf4j public class DispatchCommandLineRunner implements CommandLineRunner {@Autowiredprivate Scheduler scheduler;@Autowiredprivate ScheduleJobMapper scheduleJobMapper;@Overridepublic void run(String... args) throws Exception {log.info("開始進行定時任務初始化...");//查詢定時任務表schedule_job中所有的數據List<ScheduleJobEntity> list = scheduleJobMapper.selectList(null);//查詢數據庫for (ScheduleJobEntity scheduleJobEntity : list) {//獲得觸發器對象,調用ScheduleUtils中的方法CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJobEntity.getId());if(cronTrigger == null){//觸發器對象為空,說明對應的定時任務還沒有創建ScheduleUtils.createScheduleJob(scheduler,scheduleJobEntity);}else{//觸發器對象不為空,說明對應的定時任務已經存在了,此時只需要更新ScheduleUtils.updateScheduleJob(scheduler,scheduleJobEntity);}}} }

    創建、更新定時任務

    上面自定義的CommandLineRunner類中調用了工具類的方法來真正運行任務,需要寫一個定時任務工具類,方便通過jobid得到觸發器key和jobkey、獲得表達式觸發器
    在這里創建、更新定時任務、立即執行某個特定任務

    /*** 定時任務工具類** @author*/ public class ScheduleUtils {private final static String JOB_NAME = "TASK_";/*** 任務調度參數key*/public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";/*** 獲取觸發器key*/public static TriggerKey getTriggerKey(String jobId) {return TriggerKey.triggerKey(JOB_NAME + jobId);}/*** 獲取jobKey*/public static JobKey getJobKey(String jobId) {return JobKey.jobKey(JOB_NAME + jobId);}/*** 獲取表達式觸發器*/public static CronTrigger getCronTrigger(Scheduler scheduler, String jobId) {try {return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));} catch (SchedulerException e) {throw new PdException("getCronTrigger ERROR", e);}}/*** 創建定時任務*/public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {//構建job信息JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build();//表達式調度構建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();//按新的cronExpression表達式構建一個新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();//放入參數,運行時的方法可以獲取jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);scheduler.scheduleJob(jobDetail, trigger);//暫停任務if (scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()) {pauseJob(scheduler, scheduleJob.getId());}} catch (SchedulerException e) {throw new PdException("CREATE ERROR", e);}}/*** 更新定時任務*/public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());//表達式調度構建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());//按新的cronExpression表達式重新構建triggertrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();//參數trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);scheduler.rescheduleJob(triggerKey, trigger);//暫停任務if (scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()) {pauseJob(scheduler, scheduleJob.getId());}} catch (SchedulerException e) {throw new PdException("UPDATE ERROR", e);}}/*** 立即執行任務*/public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {try {//參數JobDataMap dataMap = new JobDataMap();dataMap.put(JOB_PARAM_KEY, scheduleJob);scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap);} catch (SchedulerException e) {throw new PdException("RUN ERROR", e);}}/*** 暫停任務*/public static void pauseJob(Scheduler scheduler, String jobId) {try {scheduler.pauseJob(getJobKey(jobId));} catch (SchedulerException e) {throw new PdException("PAUSE ERROR", e);}}/*** 恢復任務*/public static void resumeJob(Scheduler scheduler, String jobId) {try {scheduler.resumeJob(getJobKey(jobId));} catch (SchedulerException e) {throw new PdException("RESUME ERROR", e);}}/*** 刪除定時任務*/public static void deleteScheduleJob(Scheduler scheduler, String jobId) {try {scheduler.deleteJob(getJobKey(jobId));} catch (SchedulerException e) {throw new PdException("DELETE ERROR", e);}} }

    service層

    @Service public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJobEntity> implements IScheduleJobService {@Autowiredprivate Scheduler scheduler;// 在service層中調用ScheduleUtils.run的方法來立即執行任務 @Override@Transactional(rollbackFor = Exception.class)public void run(String[] ids) {for (String id : ids) {ScheduleUtils.run(scheduler, baseMapper.selectById(id));}}}

    在controller中調用service的方法

    @PutMapping("/run/{id}")@ApiOperation("立即執行")public Result run(@PathVariable String id) {scheduleJobService.run(new String[]{id});return Result.ok();}

    自定義QuartzJobBean

    【QuartzJobBean】:
    Quartz Job 接口的簡單實現,應用傳入的 JobDataMap 和 SchedulerContext 作為 bean 屬性值。 這是合適的,因為每次執行都會創建一個新的 Job 實例。 JobDataMap 條目將使用相同的鍵覆蓋 SchedulerContext 條目。
    例如,假設 JobDataMap 包含一個值為“5”的鍵“myParam”:然后,Job 實現可以公開一個 int 類型的 bean 屬性“myParam”來接收這樣的值,即方法“setMyParam(int)” . 這也適用于復雜類型,如業務對象等。
    在上面的 ScheduleUtils中使用JobBuilder.newJob(ScheduleJob.class)構建了job信息,需要拓展QuartzJobBean自定義一個定時任務類
    ScheduleJob 繼承自QuartzJobBean,程序會進入executeInternal來執行定時任務

    /*** 定時任務類,進行智能調度操作*/ public class ScheduleJob extends QuartzJobBean {private Logger logger = LoggerFactory.getLogger(getClass());@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {ScheduleJobEntity scheduleJob =(ScheduleJobEntity) context.getMergedJobDataMap().get(ScheduleUtils.JOB_PARAM_KEY);System.out.println(new Date() + "定時任務開始執行...,定時任務id:" + scheduleJob.getId());//記錄定時任務相關的日志信息//封裝日志對象ScheduleJobLogEntity logEntity = new ScheduleJobLogEntity();logEntity.setId(IdUtils.get());logEntity.setJobId(scheduleJob.getId());logEntity.setBeanName(scheduleJob.getBeanName());logEntity.setParams(scheduleJob.getParams());logEntity.setCreateDate(new Date());long startTime = System.currentTimeMillis();try{//通過反射調用目標對象,在目標對象中封裝智能調度核心邏輯logger.info("定時任務準備執行,任務id為:{}",scheduleJob.getId());//獲得目標對象Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());//獲得目標方法對象Method method = target.getClass().getDeclaredMethod("run", String.class, String.class, String.class, String.class);//通過反射調用目標對象的方法method.invoke(target,scheduleJob.getBusinessId(),scheduleJob.getParams(),scheduleJob.getId(),logEntity.getId());logEntity.setStatus(1);//成功}catch (Exception ex){logEntity.setStatus(0);//失敗logEntity.setError(ExceptionUtils.getErrorStackTrace(ex));logger.error("定時任務執行失敗,任務id為:{}",scheduleJob.getId());}finally {int times = (int) (System.currentTimeMillis() - startTime);logEntity.setTimes(times);//耗時IScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(IScheduleJobLogService.class);scheduleJobLogService.save(logEntity);}} }

    智能調度組件

    編寫智能調度組件DispatchTask,封裝的是智能調度的核心邏輯,此類中需要提供run方法,在前面的ScheduleJob定時任務中通過反射調用此run方法來完成智能調度。

    ? /*** 智能調度組件*/ @Slf4j @Component("dispatchTask") public class DispatchTask{/*** 智能調度* @param businessId* @param params* @param jobId* @param logId*/public void run(String businessId, String params, String jobId, String logId) {log.info("智能調度正在執行,參數為:{},{},{},{}", businessId, params, jobId, logId);} }

    定時任務實體類:

    @Data @EqualsAndHashCode(callSuper = false) @TableName("schedule_job") public class ScheduleJobEntity implements Serializable {/*** id*/@TableId(value = "id", type = IdType.INPUT)private String id;/*** 創建者*/@TableField(fill = FieldFill.INSERT)private Long creator;/*** 創建時間*/@TableField(fill = FieldFill.INSERT)private Date createDate;/*** spring bean名稱*/private String beanName;/*** 參數*/private String params;/*** cron表達式*/private String cronExpression;/*** 任務狀態 0:暫停 1:正常*/private Integer status;/*** 備注*/private String remark;/*** 業務id 機構id*/private String businessId;/*** 更新者*/@TableField(fill = FieldFill.INSERT_UPDATE)private Long updater;/*** 更新時間*/@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateDate; }

    mapper接口:

    /*** 定時任務 Mapper 接口*/ @Component @Mapper public interface ScheduleJobMapper extends BaseMapper<ScheduleJobEntity> {}

    定時任務在配置成功后啟動時會查詢數據庫,在控制臺輸出類似如下的相應內容:(會查詢數據庫,執行用戶定義的定時任務)

    時間觸發器的一些寫法

    具體時間設定可參考
    “0/10 * * * * ?” 每10秒觸發
    “0 0 12 * * ?” 每天中午12點觸發
    “0 15 10 ? * *” 每天上午10:15觸發
    “0 15 10 * * ?” 每天上午10:15觸發
    "0 15 10 * * ? " 每天上午10:15觸發
    “0 15 10 * * ? 2005” 2005年的每天上午10:15觸發
    “0 * 14 * * ?” 在每天下午2點到下午2:59期間的每1分鐘觸發
    “0 0/5 14 * * ?” 在每天下午2點到下午2:55期間的每5分鐘觸發
    “0 0/5 14,18 * * ?” 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
    “0 0-5 14 * * ?” 在每天下午2點到下午2:05期間的每1分鐘觸發
    “0 10,44 14 ? 3 WED” 每年三月的星期三的下午2:10和2:44觸發
    “0 15 10 ? * MON-FRI” 周一至周五的上午10:15觸發
    “0 15 10 15 * ?” 每月15日上午10:15觸發
    “0 15 10 L * ?” 每月最后一日的上午10:15觸發
    “0 15 10 ? * 6L” 每月的最后一個星期五上午10:15觸發
    “0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最后一個星期五上午10:15觸發
    “0 15 10 ? * 6#3” 每月的第三個星期五上午10:15觸發
    每隔5秒執行一次:/5 * * * * ?
    每隔1分鐘執行一次:0 */1 * * * ?
    每天23點執行一次:0 0 23 * * ?
    每天凌晨1點執行一次:0 0 1 * * ?
    每月1號凌晨1點執行一次:0 0 1 1 * ?
    每月最后一天23點執行一次:0 0 23 L * ?
    每周星期天凌晨1點實行一次:0 0 1 ? * L
    在26分、29分、33分執行一次:0 26,29,33 * * * ?
    每天的0點、13點、18點、21點都執行一次:0 0 0,13,18,21 * * ?

    總結

    以上是生活随笔為你收集整理的Quartz详解和使用CommandLineRunner在项目启动时初始化定时任务的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。