springboot結合quartz實現可在前端設置時間的定時任務


做項目時遇到一個需求,需要在web后台管理頁面設置開關燈時間,然后實現燈源的自動開關操作

首先想到的是springboot自帶的@Schedule注解,但是有個問題,因為我要在前端設置時間,而注解的實現方法只能是寫一個死的cron表達式在那兒,要更改的話只能改代碼,顯然不符合要求

於是用了quartz,實現的效果是每十秒從數據庫獲取一次cron表達式,如果和當前定時任務的cron不同的話,重新設置定時任務,這樣的話,前端就只用調用數據庫的修改接口,就可以實現設置新的定時任務

pom.xml,springboot需要的就不多說了,這里是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>

然后在service里寫任務的具體邏輯,這里是關掉所有燈,注意要加上@EnableScheduling

@Service
@EnableScheduling
public class QuartzServiceImpl implements QuartzService {
    @Autowired
    private QuartzConfigDao quartzConfigDao;

    @Autowired
    private LampDao lampDao;

    @Override
    public void closeAll() {
        lampDao.TurnOffAll();
        System.out.println("close all");
    }

}

然后是quartz相關的定時任務注冊代碼

public class QuartzConfig {
    /*
    配置定時任務
     */
    @Bean(name = "jobDetailAfterWork")
    public MethodInvokingJobDetailFactoryBean detailFactoryBean(QuartzService task){ //這里的參數就是我們剛才寫的QuartzService的一個實例
        MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
        /*
         * 是否並發執行
         * 例如每5s執行一次任務,但是當前任務還沒有執行完,就已經過了5s了,
         * 如果此處為true,則下一個任務會執行,如果此處為false,則下一個任務會等待上一個任務執行完后,再開始執行
         */
        jobDetail.setConcurrent(false);
        //設置定時任務的名字
        jobDetail.setName("AfterWork");
        //設置任務的分組,這些屬性都可以在數據庫中,在多任務的時候使用
        jobDetail.setGroup("srd");

        //為需要執行的實體類對應的對象
        jobDetail.setTargetObject(task);

        /*
         * closeAll為需要執行的方法
         * 通過這幾個配置,告訴JobDetailFactoryBean我們需要執行定時執行QuartzService類中的closeAll方法
         */
        jobDetail.setTargetMethod("closeAll");
        return jobDetail;
    }


    /*
     配置定時任務的觸發器,也就是什么時候觸發執行定時任務
     */
    @Bean(name = "jobTriggerAfterWork")
    public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetail jobDetailAfterWork){
        CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();
        trigger.setJobDetail(jobDetailAfterWork);
        //初始化的cron表達式(每天14:25:20觸發)
        trigger.setCronExpression("20 25 14 * * ?");
        //trigger的name
        trigger.setName("AfterWorkTrigger");
        return trigger;
    }

    /*
     定義quartz調度工廠
     */
    @Bean(name = "scheduler")
    public SchedulerFactoryBean schedulerFactoryBean(Trigger jobTriggerAfterWork){
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        //用於quartz集群,QuartzScheduler啟動時更新已存在的job
        factoryBean.setOverwriteExistingJobs(true);
        //延時啟動,應用啟動1秒后
        factoryBean.setStartupDelay(1);
        //注冊觸發器
        factoryBean.setTriggers(jobTriggerAfterWork);
        return factoryBean;
    }
}

這些做完之后,再運行項目,這時候已經有一個定時任務在工作中了,它會在每天的14:25:20執行QuartzService的closeAll方法

接下來是設置定時查詢數據庫的cron並與當前定時任務對比,進行刷新

@EnableScheduling
@Configuration
@Component
public class QuartzRefreshConfig {
    @Autowired
    private QuartzService quartzService;

    @Resource(name = "jobTriggerAfterWork")
    private CronTrigger cronTriggerAfterWork;

    @Resource(name = "scheduler")
    private Scheduler scheduler;

    /*
    每隔10s查庫,並根據查詢結果決定是否重新設置定時任務
    */

    @Scheduled(fixedRate = 10000)
    public void scheduleUpdateCronTrigger() throws SchedulerException {
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(cronTriggerAfterWork.getKey());
        //當前Trigger使用的
        String currentCron = trigger.getCronExpression();
        //從數據庫查詢出來的
        String searchCron = quartzService.getTaskById(1); //這里是數據庫的查詢操作,根據自己的情況自己寫

        if (currentCron.equals(searchCron)) {
            // 如果當前使用的cron表達式和從數據庫中查詢出來的cron表達式一致,則不刷新任務
        } else {
            //表達式調度構建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);
            //按新的cronExpression表達式重新構建trigger
            trigger = (CronTrigger) scheduler.getTrigger(cronTriggerAfterWork.getKey());
            trigger = trigger.getTriggerBuilder().withIdentity(cronTriggerAfterWork.getKey()).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新設置job執行
            scheduler.rescheduleJob(cronTriggerAfterWork.getKey(), trigger);
            currentCron = searchCron;
        }
    }
}

這樣我們就完成了可以動態更新的定時任務

對於多個任務,只需要在QuartzConfig中定義多個jobDetail和對應的Trigger,然后在調度工廠進行注冊就可以啦,注意多個任務時任務名,即jobdetail的名字和trigger的名字不要重復


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM