歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Spring 整合 Quartz 實現動態定時任務

最近項目中需要用到定時任務的功能,雖然spring 也自帶了一個輕量級的定時任務實現,但感覺不夠靈活,功能也不夠強大。在考慮之後,決定整合更為專業的Quartz來實現定時任務功能。

普通定時任務

首先,當然是添加依賴的jar文件,我的項目是maven管理的,以下的我項目的依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatis.version}</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.7.4</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>${mybatis.spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>${commons.lang.version}</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>${commons.dbcp.version}</version>
    </dependency>
    <dependency>
        <groupId>com.Oracle</groupId>
        <artifactId>ojdbc14</artifactId>
        <version>${ojdbc.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>${quartz.version}</version>
    </dependency>
</dependencies>

或許你應該看出來了,我的項目是spring整合了mybatis,目前spring的最新版本已經到了4.x系列,但是最新版的mybatis-spring的整合插件所依賴推薦的依然是spring 3.1.3.RELEASE,所以這裡沒有用spring的最新版而是用了推薦的3.1.3.RELEASE,畢竟最新版本的功能一般情況下也用不到。

至於quartz,則是用了目前的最新版2.2.1

之所以在這裡特別對版本作一下說明,是因為spring和quartz的整合對版本是有要求的。

spring3.1以下的版本必須使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然會出錯。

至於原因,則是spring對於quartz的支持實現,org.springframework.scheduling.quartz.CronTriggerBean繼承了org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是個類,而在quartz2.x系列中org.quartz.CronTrigger變成了接口,從而造成無法用spring的方式配置quartz的觸發器(trigger)。

在Spring中使用Quartz有兩種方式實現:第一種是任務類繼承QuartzJobBean,第二種則是在配置文件裡定義任務類和要執行的方法,類和方法可以是普通類。很顯然,第二種方式遠比第一種方式來的靈活。

這裡采用的就是第二種方式。

spring配置文件:

<!-- 使用MethodInvokingJobDetailFactoryBean,任務類可以不實現Job接口,通過targetMethod指定調用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="group" value="job_work"/>
    <property name="name" value="job_work_name"/>
    <!--false表示等上一個任務執行完後再開啟新的任務-->
    <property name="concurrent" value="false"/>
    <property name="targetObject">
        <ref bean="taskJob"/>
    </property>
    <property name="targetMethod">
        <value>run</value>
    </property>
</bean>

<!--  調度觸發器 -->
<bean id="myTrigger"
      class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="name" value="work_default_name"/>
    <property name="group" value="work_default"/>
    <property name="jobDetail">
        <ref bean="jobDetail" />
    </property>
    <property name="cronExpression">
        <value>0/5 * * * * ?</value>
    </property>
</bean>

<!-- 調度工廠 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="myTrigger"/>
        </list>
    </property>
</bean>
Task類則是一個普通的Java類,沒有繼承任何類和實現任何接口(當然可以用注解方式來聲明bean):

//@Component
public class DataConversionTask{

    /** 日志對象 */
    private static final Logger LOG = LoggerFactory.getLogger(DataConversionTask.class);

    public void run() {

        if (LOG.isInfoEnabled()) {
            LOG.info("數據轉換任務線程開始執行");
        }
    }
}

至此,簡單的整合大功告成,run方法將每隔5秒執行一次,因為配置了concurrent等於false,所以假如run方法的執行時間超過5秒,在執行完之前即使時間已經超過了5秒下一個定時計劃執行任務仍不會被開啟,如果是true,則不管是否執行完,時間到了都將開啟。

接下去,將實現如何動態的修改定時執行的時間,以及如何停止正在執行的任務。

順便貼一下cronExpression表達式備忘:

字段 允許值 允許的特殊字符

秒 0-59 , – * /
分 0-59 , – * /
小時 0-23 , – * /
日期 1-31 , – * ? / L W C
月份 1-12 或者 JAN-DEC , – * /
星期 1-7 或者 SUN-SAT , – * ? / L C #
年(可選) 留空, 1970-2099 , – * /
表達式意義

"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觸發
0 6 * * *                   每天早上6點
0 */2 * * *                 每兩個小時
0 23-7/2,8 * * *            晚上11點到早上8點之間每兩個小時,早上八點
0 11 4 * 1-3                每個月的4號和每個禮拜的禮拜一到禮拜三的早上11點
0 4 1 1 *                   1月1日早上4點

動態添加定時任務

前面,我們已經對Spring和Quartz用配置文件的方式進行了整合,如果需求比較簡單的話應該已經可以滿足了。但是很多時候,我們常常會遇到需要動態的添加或修改任務,而spring中所提供的定時任務組件卻只能夠通過修改xml中trigger的配置才能控制定時任務的時間以及任務的啟用或停止,這在帶給我們方便的同時也失去了動態配置任務的靈活性。我搜索了一些網上的解決方法,都沒有很好的解決這個問題,而且大多數提到的解決方案都停留在Quartz 1.x系列版本上,所用到的代碼和API已經不能適用於新版本的Spring和Quartz。沒辦法只能靠自己了,花了點時間好好研究了一下Spring和Quartz中相關的代碼。

首先我們來回顧一下spring中使用quartz的配置代碼:

<!-- 使用MethodInvokingJobDetailFactoryBean,任務類可以不實現Job接口,通過targetMethod指定調用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="group" value="job_work"/>
    <property name="name" value="job_work_name"/>
    <!--false表示等上一個任務執行完後再開啟新的任務-->
    <property name="concurrent" value="false"/>
    <property name="targetObject">
        <ref bean="taskJob"/>
    </property>
    <property name="targetMethod">
        <value>execute</value>
    </property>
</bean>

<!--  調度觸發器 -->
<bean id="myTrigger"
      class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="name" value="work_default_name"/>
    <property name="group" value="work_default"/>
    <property name="jobDetail">
        <ref bean="jobDetail" />
    </property>
    <property name="cronExpression">
        <value>0/5 * * * * ?</value>
    </property>
</bean>

<!-- 調度工廠 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="myTrigger"/>
        </list>
    </property>
</bean>

所有的配置都在xml中完成,包括cronExpression表達式,十分的方便。但是如果我的任務信息是保存在數據庫的,想要動態的初始化,而且任務較多的時候不是得有一大堆的xml配置?或者說我要修改一下trigger的表達式,使原來5秒運行一次的任務變成10秒運行一次,這時問題就來了,試過在配置文件中不傳入cronExpression等參數,但是啟動時就報錯了,難道我每次都修改xml文件然後重啟應用嗎,這顯然不合適的。最理想的是在與spring整合的同時又能實現動態任務的添加、刪除及修改配置。

我們來看一下spring實現quartz的方式,先看一下上面配置文件中定義的jobDetail。其實上面生成的jobDetail並不是我們定義的Bean,因為在Quartz 2.x版本中JobDetail已經是一個接口(當然以前的版本也並非直接生成JobDetail):

public interface JobDetail extends Serializable, Cloneable {…}
Spring是通過將其轉換為MethodInvokingJob或StatefulMethodInvokingJob類型來實現的,這兩個都是靜態的內部類,MethodInvokingJob類繼承於QuartzJobBean,而StatefulMethodInvokingJob則直接繼承於MethodInvokingJob。 這兩個類的實現區別在於有狀態和無狀態,對應於quartz的Job和StatefulJob,具體可以查看quartz文檔,這裡不再贅述。先來看一下它們實現的QuartzJobBean的主要代碼:

/**
 * This implementation applies the passed-in job data map as bean property
 * values, and delegates to <code>executeInternal</code> afterwards.
 * @see #executeInternal
 */
public final void execute(JobExecutionContext context) throws JobExecutionException {
    try {
        // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
        Scheduler scheduler = (Scheduler) ReflectionUtils.invokeMethod(getSchedulerMethod, context);
        Map mergedJobDataMap = (Map) ReflectionUtils.invokeMethod(getMergedJobDataMapMethod, context);

        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.addPropertyValues(scheduler.getContext());
        pvs.addPropertyValues(mergedJobDataMap);
        bw.setPropertyValues(pvs, true);
    }
    catch (SchedulerException ex) {
        throw new JobExecutionException(ex);
    }
    executeInternal(context);
}

/**
 * Execute the actual job. The job data map will already have been
 * applied as bean property values by execute. The contract is
 * exactly the same as for the standard Quartz execute method.
 * @see #execute
 */
protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;
//還有MethodInvokingJobDetailFactoryBean中的代碼:

public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {
    prepare();

    // Use specific name if given, else fall back to bean name.
    String name = (this.name != null ? this.name : this.beanName);

    // Consider the concurrent flag to choose between stateful and stateless job.
    Class jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);

    // Build JobDetail instance.
    if (jobDetailImplClass != null) {
        // Using Quartz 2.0 JobDetailImpl class...
        this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass);
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this.jobDetail);
        bw.setPropertyValue("name", name);
        bw.setPropertyValue("group", this.group);
        bw.setPropertyValue("jobClass", jobClass);
        bw.setPropertyValue("durability", true);
        ((JobDataMap) bw.getPropertyValue("jobDataMap")).put("methodInvoker", this);
    }
    else {
        // Using Quartz 1.x JobDetail class...
        this.jobDetail = new JobDetail(name, this.group, jobClass);
        this.jobDetail.setVolatility(true);
        this.jobDetail.setDurability(true);
        this.jobDetail.getJobDataMap().put("methodInvoker", this);
    }

    // Register job listener names.
    if (this.jobListenerNames != null) {
        for (String jobListenerName : this.jobListenerNames) {
            if (jobDetailImplClass != null) {
                throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " +
                        "manually register a Matcher against the Quartz ListenerManager instead");
            }
            this.jobDetail.addJobListener(jobListenerName);
        }
    }

    postProcessJobDetail(this.jobDetail);
}

上面主要看我們目前用的Quartz 2.0版本的實現部分,到這裡或許你已經明白Spring對Quartz的封裝原理了。Spring就是通過這種方式在最後Job真正執行時反調用到我們所注入的類和方法。

現在,理解了Spring的實現原理後,我們就可以來設計我們自己的了。在設計時我想到以下幾點:

1、減少spring的配置文件,為了實現一個定時任務,spring的配置代碼太多了。

2、用戶可以通過頁面等方式添加、啟用、禁用某個任務。

3、用戶可以修改某個已經在運行任務的運行時間表達式,CronExpression。

4、為方便維護,簡化任務的運行調用處理,任務的運行入口即Job實現類最好只有一個,該Job運行類相當於工廠類,在實際調用時把任務的相關信息通過參數方式傳入,由該工廠類根據任務信息來具體執行需要的操作。

在上面的思路下來進行我們的開發吧。

一、spring配置文件

通過研究,發現要實現我們的功能,只需要以下配置:


二、任務運行入口,即Job實現類,在這裡我把它看作工廠類:

/**
 * 定時任務運行工廠類
 * 
 * @author tq
 * @date 2016/5/1
 */
public class QuartzJobFactory implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("任務成功運行");
        ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");
        System.out.println("任務名稱 = [" + scheduleJob.getJobName() + "]");
    }
}

這裡我們實現的是無狀態的Job,如果要實現有狀態的Job在以前是實現StatefulJob接口,在我使用的quartz 2.2.1中,StatefulJob接口已經不推薦使用了,換成了注解的方式,只需要給你實現的Job類加上注解@DisallowConcurrentExecution即可實現有狀態:

/**
* 定時任務運行工廠類
 * @author tq
 * @date 2016/5/1
*/
@DisallowConcurrentExecution
public class QuartzJobFactory implements Job {...}

三、創建任務

既然要動態的創建任務,我們的任務信息當然要保存在某個地方了,這裡我們新建一個保存任務信息對應的實體類:

/**
 * 計劃任務信息
 * 
 * @author tq
 * @date 2016/5/1
 */
public class ScheduleJob {
    /** 任務id */
    private String jobId;
    /** 任務名稱 */
    private String jobName;
    /** 任務分組 */
    private String jobGroup;
    /** 任務狀態 0禁用 1啟用 2刪除*/
    private String jobStatus;
    /** 任務運行時間表達式 */
    private String cronExpression;
    /** 任務描述 */
    private String desc;
    getter and setter ....
}

接下來我們創建測試數據,實際應用中該數據可以保存在數據庫等地方,我們把任務的分組名+任務名作為任務的唯一key,和quartz中的實現方式一致:

/** 計劃任務map */
private static Map<String, ScheduleJob> jobMap = new HashMap<String, ScheduleJob>();

static {
    for (int i = 0; i < 5; i++) {
        ScheduleJob job = new ScheduleJob();
        job.setJobId("10001" + i);
        job.setJobName("data_import" + i);
        job.setJobGroup("dataWork");
        job.setJobStatus("1");
        job.setCronExpression("0/5 * * * * ?");
        job.setDesc("數據導入任務");
        addJob(job);
    }
}

/**
 * 添加任務
 * @param scheduleJob
 */
public static void addJob(ScheduleJob scheduleJob) {
    jobMap.put(scheduleJob.getJobGroup() + "_" + scheduleJob.getJobName(), scheduleJob);
}

有了調度工廠,有了任務運行入口實現類,有了任務信息,接下來就是創建我們的定時任務了,在這裡我把它設計成一個Job對應一個trigger,兩者的分組及名稱相同,方便管理,條理也比較清晰,在創建任務時如果不存在新建一個,如果已經存在則更新任務,主要代碼如下:

//schedulerFactoryBean 由spring創建注入
Scheduler scheduler = schedulerFactoryBean.getScheduler();

//這裡獲取任務信息數據
List<ScheduleJob> jobList = DataWorkContext.getAllJob();

for (ScheduleJob job : jobList) {

    TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());

    //獲取trigger,即在spring配置文件中定義的 bean id="myTrigger"
    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

    //不存在,創建一個
    if (null == trigger) {
        JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
            .withIdentity(job.getJobName(), job.getJobGroup()).build();
        jobDetail.getJobDataMap().put("scheduleJob", job);

        //表達式調度構建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
            .getCronExpression());

        //按新的cronExpression表達式構建一個新的trigger
        trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();

        scheduler.scheduleJob(jobDetail, trigger);
    } else {
        // Trigger已存在,那麼更新相應的定時設置
        //表達式調度構建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
            .getCronExpression());

        //按新的cronExpression表達式重新構建trigger
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
            .withSchedule(scheduleBuilder).build();

        //按新的trigger重新設置job執行
        scheduler.rescheduleJob(triggerKey, trigger);
    }
}

如此,可以說已經完成了我們的動態任務創建,大功告成了。有了上面的代碼,添加和修改任務是不是也會了,順道解決了?

上面我們創建的5個測試任務,都是5秒執行一次,都將調用QuartzJobFactory的execute方法,但是傳入的任務信息參數不同,execute方法中的如下代碼就是得到具體的任務信息,包括任務分組和任務名:

ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get(“scheduleJob”);
有了任務分組和任務名即確定了該任務的唯一性,接下來需要什麼操作實現起來是不是就很容易了?

以後需要添加新的定時任務只需要在任務信息列表中加入記錄即可,然後在execute方法中通過判斷任務分組和任務名來實現你具體的操作。

以上已經初始實現了我們需要的功能,增加和修改也已經可以通過源代碼舉一反三出來,但是我們在實際開發的時候需要進行測試,如果一個任務是1個小時運行一次的,測試起來是不是很不方便?當然你可以修改任務的運行時間表達式,但相信這不是最好的方法,接下來我們就要實現在不對當前任務信息做任何修改的情況下觸發任務,並且該觸發只會運行一次作測試用。

動態暫停 恢復 修改和刪除任務

前面我們已經完成了spring 3和quartz 2的整合以及動態添加定時任務,我們接著來完善它,使之能支持更多的操作,例如暫停、恢復、修改等。

在動態添加定時任務中其實已經涉及到了其中的一些代碼,這裡我們再來細化的理一理。先來看一下我們初步要實現的目標效果圖,這裡我們只在內存中操作,並沒有把quartz的任何信息保存到數據庫,即使用的是RAMJobStore,當然如果你有需要,可以實現成JDBCJobStore,那樣任務信息將會更全面。

trigger各狀態說明:

None:Trigger已經完成,且不會在執行,或者找不到該觸發器,或者Trigger已經被刪除
NORMAL:正常狀態
PAUSED:暫停狀態
COMPLETE:觸發器完成,但是任務可能還正在執行中
BLOCKED:線程阻塞狀態
ERROR:出現錯誤

計劃中的任務

指那些已經添加到quartz調度器的任務,因為quartz並沒有直接提供這樣的查詢接口,所以我們需要結合JobKey和Trigger來實現,核心代碼:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
for (JobKey jobKey : jobKeys) {
    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
    for (Trigger trigger : triggers) {
        ScheduleJob job = new ScheduleJob();
        job.setJobName(jobKey.getName());
        job.setJobGroup(jobKey.getGroup());
        job.setDesc("觸發器:" + trigger.getKey());
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        job.setJobStatus(triggerState.name());
        if (trigger instanceof CronTrigger) {
            CronTrigger cronTrigger = (CronTrigger) trigger;
            String cronExpression = cronTrigger.getCronExpression();
            job.setCronExpression(cronExpression);
        }
        jobList.add(job);
    }
}

上面代碼中的jobList就是我們需要的計劃中的任務列表,需要注意一個job可能會有多個trigger的情況,在下面講到的立即運行一次任務的時候,會生成一個臨時的trigger也會出現在這。這裡把一個Job有多個trigger的情況看成是多個任務。我們前面包括在實際項目中一般用到的都是CronTrigger ,所以這裡我們著重處理了下CronTrigger的情況。

運行中的任務

實現和計劃中的任務類似,核心代碼:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
    ScheduleJob job = new ScheduleJob();
    JobDetail jobDetail = executingJob.getJobDetail();
    JobKey jobKey = jobDetail.getKey();
    Trigger trigger = executingJob.getTrigger();
    job.setJobName(jobKey.getName());
    job.setJobGroup(jobKey.getGroup());
    job.setDesc("觸發器:" + trigger.getKey());
    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
    job.setJobStatus(triggerState.name());
    if (trigger instanceof CronTrigger) {
        CronTrigger cronTrigger = (CronTrigger) trigger;
        String cronExpression = cronTrigger.getCronExpression();
        job.setCronExpression(cronExpression);
    }
    jobList.add(job);
}

暫停任務

這個比較簡單,核心代碼:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.pauseJob(jobKey);

恢復任務

和暫停任務相對,核心代碼:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.resumeJob(jobKey);

刪除任務

刪除任務後,所對應的trigger也將被刪除

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.deleteJob(jobKey);

立即運行任務

這裡的立即運行,只會運行一次,方便測試時用。quartz是通過臨時生成一個trigger的方式來實現的,這個trigger將在本次任務運行完成之後自動刪除。trigger的key是隨機生成的,例如:DEFAULT.MT_4k9fd10jcn9mg。在我的測試中,前面的DEFAULT.MT是固定的,後面部分才隨機生成。

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.triggerJob(jobKey);

更新任務的時間表達式

更新之後,任務將立即按新的時間表達式執行:

Scheduler scheduler = schedulerFactoryBean.getScheduler();

TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(),
    scheduleJob.getJobGroup());

//獲取trigger,即在spring配置文件中定義的 bean id="myTrigger"
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

//表達式調度構建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob
    .getCronExpression());

//按新的cronExpression表達式重新構建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
    .withSchedule(scheduleBuilder).build();

//按新的trigger重新設置job執行
scheduler.rescheduleJob(triggerKey, trigger);

Spring3.2.11與Quartz2.2.1整合時內存洩漏問題解決  http://www.linuxidc.com/Linux/2015-06/119042.htm

Spring配置Quartz任務調度框架教程  http://www.linuxidc.com/Linux/2014-11/108907.htm

Quartz深入淺出 http://www.linuxidc.com/Linux/2014-09/107007.htm

Quartz1.6有狀態JOB碰到的棘手問題既解決方案 http://www.linuxidc.com/Linux/2014-09/107005.htm

Spring 3整合Quartz 2實現定時任務 http://www.linuxidc.com/Linux/2014-09/107006.htm

Java項目中定時任務之Quartz的應用 http://www.linuxidc.com/Linux/2013-12/94443.htm

Spring 3 調度器示例 —— JDK 定時器和 Quartz 展示 http://www.linuxidc.com/Linux/2013-10/91946.htm

Spring Quartz定時任務  http://www.linuxidc.com/Linux/2016-11/137525.htm

Quartz框架 實現任務調度 http://www.linuxidc.com/Linux/2016-11/136778.htm

Copyright © Linux教程網 All Rights Reserved