Spring Boot定時任務應用實踐


在Spring Boot中實現定時任務功能,可以通過Spring自帶的定時任務調度,也可以通過集成經典開源組件Quartz實現任務調度。

一、Spring定時器

1、cron表達式方式

使用自帶的定時任務,非常簡單,只需要像下面這樣,加上注解就好,不需要像普通定時任務框架那樣繼承任何定時處理接口 ,簡單示例代碼如下:

package com.power.demo.scheduledtask.simple;

import com.power.demo.util.DateTimeUtil;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@EnableScheduling
public class SpringTaskA {

    /**
     * CRON表達式參考:http://cron.qqe2.com/
     **/
    @Scheduled(cron = "*/5 * * * * ?", zone = "GMT+8:00")
    private void timerCron() {

        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(String.format("(timerCron)%s 每隔5秒執行一次,記錄日志", DateTimeUtil.fmtDate(new Date())));

    }

}
SpringTaskA

上述代碼中,在一個類上添加@EnableScheduling注解,在方法上加上@Scheduled,配置下cron表達式,一個最最簡單的cron定時任務就完成了。cron表達式的各個組成部分,可以參考下面:

@Scheduled(cron = "[Seconds] [Minutes] [Hours] [Day of month] [Month] [Day of week] [Year]")

2、fixedRate和fixedDelay

@Scheduled注解除了cron表達式,還有其他配置方式,比如fixedRate和fixedDelay,下面這個示例通過配置方式的不同,實現不同形式的定時任務調度,示例代碼如下:

package com.power.demo.scheduledtask.simple;

import com.power.demo.util.DateTimeUtil;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@EnableScheduling
public class SpringTaskB {

    /*fixedRate:上一次開始執行時間點之后5秒再執行*/
    @Scheduled(fixedRate = 5000)
    public void timerFixedRate() {

        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(String.format("(fixedRate)現在時間:%s", DateTimeUtil.fmtDate(new Date())));
    }

    /*fixedDelay:上一次執行完畢時間點之后5秒再執行*/
    @Scheduled(fixedDelay = 5000)
    public void timerFixedDelay() {

        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(String.format("(fixedDelay)現在時間:%s", DateTimeUtil.fmtDate(new Date())));

    }

    /*第一次延遲2秒后執行,之后按fixedDelay的規則每5秒執行一次*/
    @Scheduled(initialDelay = 2000, fixedDelay = 5000)
    public void timerInitDelay() {

        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(String.format("(initDelay)現在時間:%s", DateTimeUtil.fmtDate(new Date())));
    }

}
SpringTaskB

注意一下主要區別:

@Scheduled(fixedRate = 5000) :上一次開始執行時間點之后5秒再執行

@Scheduled(fixedDelay = 5000) :上一次執行完畢時間點之后5秒再執行

@Scheduled(initialDelay=2000, fixedDelay=5000) :第一次延遲2秒后執行,之后按fixedDelay的規則每5秒執行一次

有時候,很多項目我們都需要配置好定時任務后立即執行一次,initialDelay就可以不用配置了。

3、zone

@Scheduled注解還有一個熟悉的屬性zone,表示時區,通常,如果不寫,定時任務將使用服務器的默認時區;如果你的任務想在特定時區特定時間點跑起來,比如常見的多語言系統可能會定時跑腳本更新數據,就可以設置一個時區,如東八區,就可以設置為:

zone = "GMT+8:00"

二、Quartz

Quartz是應用最為廣泛的開源任務調度框架之一,有很多公司都根據它實現自己的定時任務管理系統。Quartz提供了最常用的兩種定時任務觸發器,即SimpleTrigger和CronTrigger,本文以最廣泛使用的CronTrigger為例。

1、添加依賴

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>
quartz

2、配置cron表達式

示例代碼需要,在application.properties文件中新增如下配置:

## Quartz定時job配置
job.taska.cron=*/3 * * * * ?
job.taskb.cron=*/7 * * * * ?
job.taskmail.cron=*/5 * * * * ?
cron-config

其實,我們完全可以不用配置,直接在代碼里面寫或者持久化在DB中然后讀取也可以。

3、添加定時任務實現

任務1:

package com.power.demo.scheduledtask.quartz;

import com.power.demo.util.DateTimeUtil;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;


@DisallowConcurrentExecution
public class QuartzTaskA implements Job {

    @Override
    public void execute(JobExecutionContext var1) throws JobExecutionException {
        try {
            Thread.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(String.format("(QuartzTaskA)%s 每隔3秒執行一次,記錄日志", DateTimeUtil.fmtDate(new Date())));

    }
}
QuartzTaskA

任務2:

package com.power.demo.scheduledtask.quartz;

import com.power.demo.util.DateTimeUtil;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;

@DisallowConcurrentExecution
public class QuartzTaskB implements Job {

    @Override
    public void execute(JobExecutionContext var1) throws JobExecutionException {
        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(String.format("(QuartzTaskB)%s 每隔7秒執行一次,記錄日志", DateTimeUtil.fmtDate(new Date())));

    }
}
QuartzTaskB

定時發送郵件任務:

package com.power.demo.scheduledtask.quartz;

import com.power.demo.service.contract.MailService;
import com.power.demo.util.DateTimeUtil;
import com.power.demo.util.PowerLogger;
import org.joda.time.DateTime;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;


@DisallowConcurrentExecution
public class MailSendTask implements Job {

    @Autowired
    private MailService mailService;

    @Override
    public void execute(JobExecutionContext var1) throws JobExecutionException {

        System.out.println(String.format("(MailSendTask)%s 每隔5秒發送郵件", DateTimeUtil.fmtDate(new Date())));

        try {
            //Thread.sleep(1);
            DateTime dtNow = new DateTime(new Date());

            Date startTime = dtNow.minusMonths(1).toDate();//一個月前

            Date endTime = dtNow.plusDays(1).toDate();

            mailService.autoSend(startTime, endTime);

            PowerLogger.info(String.format("發送郵件,開始時間:%s,結束時間:%s"
                    , DateTimeUtil.fmtDate(startTime), DateTimeUtil.fmtDate(endTime)));

        } catch (Exception e) {
            e.printStackTrace();
            PowerLogger.info(String.format("發送郵件,出現異常:%s,結束時間:%s", e));
        }

    }
}
MailSendTask

實現任務看上去非常簡單,繼承Quartz的Job接口,重寫execute方法即可。

4、集成Quartz定時任務

怎么讓Spring自動識別初始化Quartz定時任務實例呢?這就需要引用Spring管理的Bean,向Spring容器暴露所必須的bean,通過定義Job Factory實現自動注入。

首先,添加Spring注入的Job Factory類:

package com.power.demo.scheduledtask.quartz.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

public final class AutowireBeanJobFactory extends SpringBeanJobFactory
        implements ApplicationContextAware {

    private transient AutowireCapableBeanFactory beanFactory;

    /**
     * Spring提供了一種機制讓你可以獲取ApplicationContext,即ApplicationContextAware接口
     * 對於一個實現了ApplicationContextAware接口的類,Spring會實例化它的同時調用它的
     * public voidsetApplicationContext(ApplicationContext applicationContext) throws BeansException;接口,
     * 將該bean所屬上下文傳遞給它。
     **/
    @Override
    public void setApplicationContext(final ApplicationContext context) {
        beanFactory = context.getAutowireCapableBeanFactory();
    }

    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle)
            throws Exception {
        final Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);
        return job;
    }
}
AutowireBeanJobFactory

定義QuartzConfig:

package com.power.demo.scheduledtask.quartz.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
public class QuartzConfig {

    @Autowired
    @Qualifier("quartzTaskATrigger")
    private CronTriggerFactoryBean quartzTaskATrigger;

    @Autowired
    @Qualifier("quartzTaskBTrigger")
    private CronTriggerFactoryBean quartzTaskBTrigger;

    @Autowired
    @Qualifier("mailSendTrigger")
    private CronTriggerFactoryBean mailSendTrigger;

    //Quartz中的job自動注入spring容器托管的對象
    @Bean
    public AutowireBeanJobFactory autoWiringSpringBeanJobFactory() {
        return new AutowireBeanJobFactory();
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
        scheduler.setJobFactory(autoWiringSpringBeanJobFactory());  //配置Spring注入的Job類

        //設置CronTriggerFactoryBean,設定任務Trigger
        scheduler.setTriggers(
                quartzTaskATrigger.getObject(),
                quartzTaskBTrigger.getObject(),
                mailSendTrigger.getObject()
        );
        return scheduler;
    }

}
QuartzConfig

接着配置job明細:

package com.power.demo.scheduledtask.quartz.config;

import com.power.demo.common.AppField;
import com.power.demo.scheduledtask.quartz.MailSendTask;
import com.power.demo.scheduledtask.quartz.QuartzTaskA;
import com.power.demo.scheduledtask.quartz.QuartzTaskB;
import com.power.demo.util.ConfigUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;

@Configuration
public class TaskSetting {

    @Bean(name = "quartzTaskA")
    public JobDetailFactoryBean jobDetailAFactoryBean() {
        //生成JobDetail
        JobDetailFactoryBean factory = new JobDetailFactoryBean();
        factory.setJobClass(QuartzTaskA.class);  //設置對應的Job
        factory.setGroup("quartzTaskGroup");
        factory.setName("quartzTaskAJob");
        factory.setDurability(false);
        factory.setDescription("測試任務A");

        return factory;
    }

    @Bean(name = "quartzTaskATrigger")
    public CronTriggerFactoryBean cronTriggerAFactoryBean() {

        String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKA_CRON);

        CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
        //設置JobDetail
        stFactory.setJobDetail(jobDetailAFactoryBean().getObject());
        stFactory.setStartDelay(1000);
        stFactory.setName("quartzTaskATrigger");
        stFactory.setGroup("quartzTaskGroup");
        stFactory.setCronExpression(cron);

        return stFactory;
    }

    @Bean(name = "quartzTaskB")
    public JobDetailFactoryBean jobDetailBFactoryBean() {
        //生成JobDetail
        JobDetailFactoryBean factory = new JobDetailFactoryBean();
        factory.setJobClass(QuartzTaskB.class);  //設置對應的Job
        factory.setGroup("quartzTaskGroup");
        factory.setName("quartzTaskBJob");
        factory.setDurability(false);
        factory.setDescription("測試任務B");

        return factory;
    }

    @Bean(name = "quartzTaskBTrigger")
    public CronTriggerFactoryBean cronTriggerBFactoryBean() {

        String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKB_CRON);

        CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
        //設置JobDetail
        stFactory.setJobDetail(jobDetailBFactoryBean().getObject());
        stFactory.setStartDelay(1000);
        stFactory.setName("quartzTaskBTrigger");
        stFactory.setGroup("quartzTaskGroup");
        stFactory.setCronExpression(cron);

        return stFactory;
    }

    @Bean(name = "mailSendTask")
    public JobDetailFactoryBean jobDetailMailFactoryBean() {
        //生成JobDetail
        JobDetailFactoryBean factory = new JobDetailFactoryBean();
        factory.setJobClass(MailSendTask.class);  //設置對應的Job
        factory.setGroup("quartzTaskGroup");
        factory.setName("mailSendTaskJob");
        factory.setDurability(false);
        factory.setDescription("郵件發送任務");

        return factory;
    }

    @Bean(name = "mailSendTrigger")
    public CronTriggerFactoryBean cronTriggerMailFactoryBean() {

        String cron = ConfigUtil.getConfigVal(AppField.JOB_TASKMAIL_CRON);

        CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
        //設置JobDetail
        stFactory.setJobDetail(jobDetailMailFactoryBean().getObject());
        stFactory.setStartDelay(1000);
        stFactory.setName("mailSendTrigger");
        stFactory.setGroup("quartzTaskGroup");
        stFactory.setCronExpression(cron);

        return stFactory;
    }

}
TaskSetting

最后啟動你的Spring Boot定時任務應用,一個完整的基於Quartz調度的定時任務就實現好了。

本文定時任務示例中,有一個定時發送郵件任務MailSendTask,下一篇將分享Spring Boot應用中以MongoDB作為存儲介質的簡易郵件系統。

 

擴展閱讀:

很多公司都會有自己的定時任務調度框架和系統,在Spring Boot中如何整合Quartz集群,實現動態定時任務配置?

參考:

http://www.cnblogs.com/vincent0928/p/6294792.html

http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html

 


免責聲明!

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



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