Spring 和Quartz2 整合實現動態定時任務

jopen 9年前發布 | 61K 次閱讀 Spring JEE框架


問題起始:

        最近要做一個定時任務,使用Spring的定時任務配置也可以實現。但是很多時候,我們常常會遇到需要動態的添加或修改任務,而spring中所提供的定時任務組件卻只能夠通過修改xml中trigger的配置才能控制定時任務的時間以及任務的啟用或停止,這在帶給我們方便的同時也失去了動態配置任務的靈活性。我搜索了一些網上的解決方法,都沒有很好的解決這個問題,而且大多數提到的解決方案都停留在Quartz 1.x系列版本上,所用到的代碼和API已經不能適用于新版本的Spring和Quartz。那么讓我們來解決它吧、

知識點補充:

      1、quartz任務調度快速入門:

          任務調度快速入門

          該資料使用 的是舊版本的quarzt,里面的實例化 jobdetail  和 Trigger的方式都不適用了。但是,對很多基礎的概念解釋的相當清晰。推薦只看概念,加深理解任務調度的工作機制。

      2、帶參數執行 任務調度

          在job中,不可能不需要參數,這時候參數的傳遞就顯得尤為重要了。quartz2提供參數傳遞方法是:

           1).jobDetail.getJobDataMap().put("timerconfig", timerConfig);   將 timerConfig 以map集合的形式傳遞給  任務執行時的上下文。

           2). (TimerConfig) context.getJobDetail().getJobDataMap().get("timerconfig");     context是job接口中execute(JobExecutionContext context);將剛剛傳遞的timerconfig取出

如果timerconfig是對象,則用get();其他的則使用對應的get方法即可。

      3、如何實例化JobDetail

       在quartz2中,實例化任務的方式變化較大,是使用builder進行實例化。 JobDetail jobDetail = newJob(MyTask.class) .withIdentity(name, Groupname).build();

      4、如何實例化Trigger

       Trigger trigger = newTrigger()
                        .withIdentity(name, Groupname)
                        .startNow()
                        .withSchedule(
                                CronScheduleBuilder
                                        .cronSchedule(new CronExpression(
                                                expression))).build();

       此版本不再采用1版本的 SimpleTrigger /cronTrigger.而是在使用調度器(schedule)的時候選擇是用 SimpleScheduleBuilder還是 CronScheduleBuilder。

       例如: CronScheduleBuilder .cronSchedule(new CronExpression( expression))//采用cronScheduleBuilder生產 cronTrigger定時器

                    SimpleScheduleBuilder .SimpleSchedule()//采用SimpleScheduleBuilder生產 SimpleTrigger定時器

     

問題解決:

       bean.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-4.0.xsd
               http://www.springframework.org/schema/tx
               http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
               ">

   <!-- 調度器  -->   
    <bean id="schedulerFactoryBean"   class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">   
    </bean>

</beans>

 


    Mytask,java

/*
 * @(#)MyTask.java  V0.0.1 2015-1-28, 下午8:34:14
 *
 */
package com.jpgk.system.timer.config;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import com.jpgk.mq.model.TimerConfig;

public class MyTask implements Job {
    private String id;
    private String category;
    private String destination;
    private String uri;
    private String clientid;
    private String content;
    private String expression;
    private String method;
    private TimerConfig timerConfig;// 一組timerconfig數據對象

    // 任務執行覆寫
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        TimerConfig timerConfig = (TimerConfig) context.getJobDetail()
                .getJobDataMap().get("timerconfig");
        // 屬性賦值
        initPrivate(timerConfig);
        System.out.println(timerConfig.getContent());
    }

    // 初始化私有屬性,這個方法的存在是為了解決 線程池在每次反射實例化MyTask的時候使用無參構造函數,但任務需要這些私有屬性作為任務執行的參數
    public void initPrivate(TimerConfig timerconfig) {
        timerConfig = timerconfig;
        category = timerConfig.getCategory().toUpperCase();// 取出任務類型
        uri = timerConfig.getUri();// 取出請求路徑
        destination = timerConfig.getDestination();// 取出目的地
        clientid = timerConfig.getClientid();// 客戶ID
        expression = timerConfig.getExpression();// 表達式
        content = timerConfig.getContent();// 請求參數,例如 a=1&b=2
        method = timerConfig.getMethod().toUpperCase();
    }

}

 調度頁面:Testabc.java

/*
 * @(#)Testabc.java V0.0.1 2015-1-28, 下午8:06:03
 *
 * Copyright 2015 www.ifood517.com. All rights reserved.
 * www.ifood517.com PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.jpgk.mq.temp;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdScheduler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jpgk.mq.model.TimerConfig;
import com.jpgk.mq.service.TimerConfigService;
import com.jpgk.system.timer.config.MyTask;

public class Testabc {

    private static StdScheduler stdScheduler;

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext(
                "classpath:applicationContext.xml");
        // 實例化線程池
        stdScheduler = (StdScheduler) context.getBean("schedulerFactoryBean");
        // 取出數據庫配置信息
        TimerConfigService timerConfigService = (TimerConfigService) context
                .getBean("timerstaskservice");
        List<TimerConfig> configs = timerConfigService.selectAll();

        for (int i = 0; i < configs.size(); i++) {
            // 進行任務的調度
            /*
             * String category = configs.get(i).getCategory();// 取出任務類型 String
             * uri = configs.get(i).getUri();// 取出請求路徑 String destination =
             * configs.get(i).getDestination();// 取出目的地 String clientid =
             * configs.get(i).getClientid();// 客戶ID String expression =
             * configs.get(i).getExpression();// 表達式 String content =
             * configs.get(i).getContent();// 請求參數,例如 a=1&b=2 String method =
             * configs.get(i).getMethod().toUpperCase();//請求方式
             */switch (configs.get(i).getCategory()) {
            case "HTTP":
                // 必須要請求路徑
                if (configs.get(i).getUri() == ""
                        || configs.get(i).getUri() == null) {
                    try {
                        throw (new Exception("請求地址不能為空"));
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                // 必須要請頻率
                if (configs.get(i).getExpression() == ""
                        || configs.get(i).getExpression() == null) {
                    try {
                        throw (new Exception("執行頻率不能為空"));
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if (configs.get(i).getMethod().toUpperCase().equals("GET")) {
                    // 重新構造uri
                    configs.get(i).setUri(
                            configs.get(i).getUri() + "?"
                                    + configs.get(i).getContent());
                } else {
                    // post須考慮其他方式
                    System.out.println("aaaa");
                }
                // System.out.println("category->"+category+"uri->"+uri+"destination->"+destination+"clientid->"+clientid+"expression->"+expression+"content->"+content+"method->"+method);
                break;
            default:
                break;
            }

            // 構造任務
            JobDetail jobDetail = initJobdetail(configs.get(i).getClientid(),
                    configs.get(i).getClientid() + "Group", configs.get(i));
            // 構造定時器
            Trigger trigger = initTriger(2, configs.get(i).getClientid(),
                    configs.get(i).getClientid() + "Group", configs.get(i)
                            .getExpression());
            // 注冊定時器和任務
            try {
                stdScheduler.scheduleJob(jobDetail, trigger);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 實例化一個Trigger,根據type返回simple/Cron Trigger
     */
    private static Trigger initTriger(int type, String name, String Groupname,
            String expression) {
        try {
            if (type == 1) {
                // 1 simpleTrigger
                // Simp
            } else if (type == 2) {
                // conTrigger
                Trigger trigger = newTrigger()
                        .withIdentity(name, Groupname)
                        .startNow()
                        .withSchedule(
                                CronScheduleBuilder
                                        .cronSchedule(new CronExpression(
                                                expression))).build();
                return trigger;
            }
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 實例化一個JobDetail
     */
    private static JobDetail initJobdetail(String name, String Groupname,
            TimerConfig timerConfig) {
        JobDetail jobDetail = newJob(MyTask.class)
                .withIdentity(name, Groupname).build();
        // 在每次添加任務的時候要添加額外的參數,這里我傳一個對象進行任務私有屬性的初始化
        jobDetail.getJobDataMap().put("timerconfig", timerConfig);
        return jobDetail;
    }

}

以上代碼涉及到數據庫的數據,忽略即可。看看如何實例化JobDetail和Trigger的。留意下怎么在JobDetail中傳遞TimerConfig參數的。



但是只能說以上代碼只是個解決思路,但是任務調度真正的知識不限于這么少。例如,在任務執行過程中強制終止,休眠,更改表達式等。

供參考:任務調度暫停等實現

來自:http://blog.csdn.net/ccyours/article/details/43273663

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!