Quartz 2.xとSpringの動的統合


一、Quartzの紹介  
    QuartzはJames Houseによって創立されたオープンソースプロジェクトであり、強力なジョブスケジューリングツールであり、計画的にタスクを実行したり、タイミング、サイクル、またはある時間に私たちがしなければならないことを実行したりすることができ、これは私たちの仕事に大きな助けをもたらすことができます.たとえば、あなたのプログラムでは毎月1番のレポートをエクスポートしたり、メールをタイミングよく送信したり、プログラムが1段おきにタスクを実行したりする必要があります......など、Quartzで解決できます.
        Quartzは大きく3つの主要なコアに分けることができます.
    1、スケジューラScheduler:計画スケジューラ容器で、容器の中に多くのJobDetailとTriggerを入れることができ、容器が起動すると、中のJobDetailはTriggerによって順番に自動的に実行する.
    2、任務Job:実行する具体的な内容.JobDetail:このタスクのスケジューリングのシナリオとポリシーを含む具体的な実行可能なスケジューラ.
    3、トリガTrigger:スケジューリングパラメータの構成、いつスケジューリングを実行するか.
    スケジューラはコンテナに相当し、タスクとトリガを搭載しているという原理が理解できます.タスクとトリガはまた結合されていますが、1つのタスクは複数のトリガに対応できますが、1つのトリガは1つのタスクにしか対応できません.JobDetailとTriggerがschedulerコンテナに登録されると、組み立てられたタスクジョブ(JobDetailとTriggerからなるペア)が形成され、コンテナの起動に伴ってスケジューリング実行される.
二、springとの統合
    本稿ではquartz-2.2.1とspring-3.2.2を用いた.ここで特にバージョンについて説明するのは、springとquartzの統合がバージョンに要求されているからです.Spring 3.1以下のバージョンではquartz 1.xシリーズを使用する必要があります.3.1以上のバージョンではquartz 2.xがサポートされていません.そうしないとエラーが発生します.理由は主にspringのquartzに対するサポート実装、org.springframework.scheduling.quartz.cronTriggerBeanはorg.quartz.CronTriggerを継承し、quartz 1.xシリーズではorg.quartz.CronTriggerがクラスであり、quartz 2.xシリーズではorg.quartz.CronTriggerが接続口となり、springではquartzを構成できないトリガ(trigger)となったからである.
    SpringでQuartzを使用するには、2つの方法があります.1つ目はタスククラスがQuartzJobBeanを継承し、2つ目はプロファイルでタスククラスと実行するメソッドを定義し、クラスとメソッドは通常のクラスです.ここでは、より使いやすい2つ目の方法を紹介します.
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を注釈で宣言することができます):
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、C*/
分0-59,�C*/
時間0-23、C*/
日付1-31、�C*?/L W C
月1~12またはJAN-DEC、�C*/
曜日1-7またはSUN-SAT、�C*?/L C#
年(オプション)を空け、1970-2099、C*/
式の意味
「0 0 12**?」毎日正午12時にトリガーされます
「0 15 10?*」は毎日午前10時15分にトリガーされます.
毎日午前10時15分にトリガーされます.
「0 15 10*?*」は毎日午前10時15分にトリガーされます.
2005年の毎日午前10時15分にトリガー
毎日午後2時から午後2時59分までの1分間にトリガーされます.
「0 0 0/5 14**?」は、毎日午後2時から午後2:55までの5分間にトリガーされます.
「0 0 0/5 14,18*?」は、毎日午後2時から2:55までの期間と午後6時から6:55までの期間の5分ごとにトリガーされます.
毎日午後2時から午後2時05分までの1分間にトリガーされます.
「0 10,44 14?3 WED」毎年3月の水曜の午後2:10と2:44にトリガー
「0 15 10?*MON-FRI」月曜日から金曜日の午前10:15にトリガー
「0 15 10 15*?」毎月15日午前10時15分にトリガー
月末日の午前10時15分にトリガー
「0 15 10?*6 L」毎月の最終金曜日午前10:15にトリガー
「0 15 10?*6 L 2002-2005」2002年から2005年までの毎月の最終金曜日午前10:15にトリガーされます.
「0 15 10?*6#3」毎月第3金曜日午前10時15分にトリガー
毎朝6時
0 6 * * *
2時間ごとに
0 */2 * * *
夜11時から朝8時まで2時間ずつ、朝8時
0 23-7/2,8 * * *
毎月の4日と毎週月曜日から水曜日の朝11時まで
0 11 4 * 1-3
1月1日午前4時
0 4 1 1 *
三、動的統合
    上記の統合は簡単なニーズにしか対応できませんが、動的な追加、一時停止、タスクの変更が必要な場合が多いです.springで提供されるタイミングタスクコンポーネントは、xmlのtriggerの構成を変更することによってのみ、タイミングタスクの時間およびタスクの有効化または停止を制御することができ、これにより、タスクの動的構成の柔軟性が失われます.
    だから私たちは別の方法で解決しなければなりません.タスクとcronExpressionをデータベースに保存し、xml構成を最大化して削減し、ファクトリクラスを作成し、実際の呼び出し時にタスクに関する情報をパラメータ方式で転送し、このファクトリクラスがタスク情報に基づいて必要な操作を具体的に実行することで、動的な修正を容易にします.
    1.springの構成(実はこの一行だけで十分で、元の「taskJob」、「myTrigger」などの構成を取り除いた):
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />

    2.タスク実行入口、Jobインタフェースを実現し、工場類に類似する:
package com.quartz;

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

import com.quartz.model.ScheduleJob;

/**
 *         
 * @author unique
 *
 */
public class QuartzJobFactory implements Job {

    public void execute(JobExecutionContext context) throws JobExecutionException {
        
         System.out.println("      ");
         ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");
         System.out.println("     = [" + scheduleJob.getJobName() + "]");
         
         //  name   group                 ……
         
    }

}

    ここで我々は無状態のJobを実現しているが、もし状態のあるJobを実現するには以前はStatefulJobインタフェースを実現していたならば、私が使用していたquartz 2.2.1では、StatefulJobインタフェースはすでに使用を推奨していないので、注釈の方式に変えて、あなたが実現したJobクラスに注釈を加えるだけである@DisallowConcurrentExecutionを実現することができる:
/** 
*           
*/
@DisallowConcurrentExecution
public class QuartzJobFactory implements Job {...}

    ステータスの違いは次のとおりです.http://fordream.iteye.com/blog/1179097
    3.タスクを作成します.タスクを動的に変更するには、タスクをどこかに保存する必要があります.そのため、タスク情報を保存するためにJavaBeanが必要です.
package com.quartz.model;

/**
 *       
 * @author unique
 *
 */
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;

    public ScheduleJob() {
        super();
    }

    public ScheduleJob(String jobId, String jobName, String jobGroup,
            String jobStatus, String cronExpression, String desc) {
        super();
        this.jobId = jobId;
        this.jobName = jobName;
        this.jobGroup = jobGroup;
        this.jobStatus = jobStatus;
        this.cronExpression = cronExpression;
        this.desc = desc;
    }

    public String getJobId() {
        return jobId;
    }

    public void setJobId(String jobId) {
        this.jobId = jobId;
    }

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
        this.jobGroup = jobGroup;
    }

    public String getJobStatus() {
        return jobStatus;
    }

    public void setJobStatus(String jobStatus) {
        this.jobStatus = jobStatus;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
    
    
}

    次に、このクラスが最も主要であり、要求アドレスが対応する方法にジャンプします.
package com.wxapi.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.quartz.QuartzJobFactory;
import com.quartz.model.ScheduleJob;
import com.wxapi.bs.IQuartzBS;

@Controller
@RequestMapping("/quartz")
public class QuartzAction {

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    
    @Autowired
    private IQuartzBS quartzBS;
    
    /**
     *        (       ,       )
     * @param request
     * @param response
     * @param scheduleJob
     * @param model
     * @return
     */
    @RequestMapping(value="/update", method={RequestMethod.POST,RequestMethod.GET})
    public String updateQuartz(HttpServletRequest request,HttpServletResponse response,
            @ModelAttribute("scheduleJob") ScheduleJob job,ModelMap model){
        
        try {
            
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            
            if(null!=job){
                
                //       
                TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
                //     trigger
                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);
                    
                    //        
                    int result = quartzBS.add(job);
                    if(result!=0){
                        model.addAttribute("msg", "        !");
                    }else{
                        model.addAttribute("msg", "        !");
                    }
                    
                }else{//    
                    
                    // Trigger   ,           
                    //        
                    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
                            .getCronExpression());
                    
                    //   cronExpression       trigger
                    trigger = trigger.getTriggerBuilder()
                            .withIdentity(triggerKey)
                            .withSchedule(scheduleBuilder)
                            .build();
                    
                    //   trigger    job  
                    scheduler.rescheduleJob(triggerKey, trigger);
                    
                    //         
                    int result = quartzBS.update(job);
                    if(result==1){
                        model.addAttribute("msg", "        !");
                    }else{
                        model.addAttribute("msg", "        !");
                    }
                }
                
            }
            
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        
        return "/warn.jsp";
    }
    
    
    /**
     *     
     * @param request
     * @param response
     * @param job
     * @param model
     * @return
     */
    @RequestMapping(value="/pause", method={RequestMethod.POST,RequestMethod.GET})
    public String pauseQuartz(HttpServletRequest request,HttpServletResponse response,
            @ModelAttribute("scheduleJob") ScheduleJob scheduleJob,ModelMap model){

        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        return "/warn.jsp";
    }
    
    
    /**
     *     
     * @param request
     * @param response
     * @param scheduleJob
     * @param model
     * @return
     */
    @RequestMapping(value="/resume", method={RequestMethod.POST,RequestMethod.GET})
    public String resumeQuartz(HttpServletRequest request,HttpServletResponse response,
            @ModelAttribute("scheduleJob") ScheduleJob scheduleJob,ModelMap model){

        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        return "/warn.jsp";
    }
    
    
    /**
     *     
     * @param request
     * @param response
     * @param scheduleJob
     * @param model
     * @return
     */
    @RequestMapping(value="/delete", method={RequestMethod.POST,RequestMethod.GET})
    public String deleteQuartz(HttpServletRequest request,HttpServletResponse response,
            @ModelAttribute("scheduleJob") ScheduleJob scheduleJob,ModelMap model){

        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        try {
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        return "/warn.jsp";
    }

}

    「プラントクラスの実行」では、次の操作を行います.
ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");

    タスクグループとタスク名を取得して、タスクの一意性を決定し、executeメソッドでタスクグループとタスク名を判断することで、特定の操作を実現できます.
四、推薦:
    推荐博客:《Quartz深入浅出》
    公式サイト:http://www.quartz-scheduler.org/
    ダウンロード:http://www.quartz-scheduler.org/downloads
五、締めくくり:
    ええと、quartzとspringのダイナミック統合はここで大雑把に説明して、特に感謝します:毎日お客様のウェブサイトの文章《Spring 3統合Quartz 2実現定時任務》があって、文の中の大部分の内容も中から参考にします.不足点があれば指摘してください.
    堅持するのは1種の精神で、分かち合うのは1種の楽しみです!