SpringMvc + Quarzt 动态执行任务实现过程

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.2.1。

本次遇到的情况是 关于N多个抓取任务的调度,并且由于各任务本身是有关联性,不易改造成标准的Jobs,并考虑到不去入侵和修改原来的任务方法,故期望运用Quartz按照Cron设定的时间去执行指定类和方法。

因为需要保存数据到数据库,设计了一个JavaBean来存储数据结构如下:

public class Quartz {
    /** 自动编号 */
    private Long              SCHEDULE_ID;
    /** 任务名称 */
    private String            NAME;
    /** 任务别名 */
    private String            ALIASNAME;
    /** 任务分组 */
    private String            GROUP;
    /** 任务执行类-全路径 */
    private String            CLASS;
    /** 任务执行类-方法 */
    private String            METHOD;
    /** 任务Cron表达式 */
    private String            CRON;
    /** 任务描述 */
    private String            DESC;
    private int               STATUS_ID;
    private Long              CREATETIME;
    private Long              UPDATETIME;
    private int               CREATEUSER;
    private int               UPDATEUSER;
}

Quartz 将作业划分为:执行器(JobDetail)、触发器(Trigger)、监听器(Listener),简略说就是执行器决定如何执行任务,触发器决定何时执行任务,监听器决定任务到达某状态如何操作。

Quartz典型的JobDetail是实现一个Job接口,然后新建一个触发器去执行该Job。

典型的Quartz应用一:

import java.util.Date;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.quartz.Job;  
import org.quartz.JobExecutionContext;  
import org.quartz.JobExecutionException;  

public class HelloJob implements Job {  

    private static Logger _log = LoggerFactory.getLogger(HelloJob.class);  

    public HelloJob() {  

    }  

    public void execute(JobExecutionContext context)  
        throws JobExecutionException {  

        _log.error(" 执行任务: " + new Date());  

    }  
}  

测试类

import static org.quartz.JobBuilder.newJob;  
import static org.quartz.TriggerBuilder.newTrigger;  
import static org.quartz.DateBuilder.*;  
import java.util.Date;  
import org.quartz.JobDetail;  
import org.quartz.Scheduler;  
import org.quartz.SchedulerFactory;  
import org.quartz.Trigger;  
import org.quartz.impl.StdSchedulerFactory;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
public class SimpleExample {  

    private static Logger log = LoggerFactory.getLogger(SimpleExample.class);  

    public void run() throws Exception {  

        // 通过SchedulerFactory获取一个调度器实例  
        SchedulerFactory sf = new StdSchedulerFactory();      

        Scheduler sched = sf.getScheduler();  

        Date runTime = evenMinuteDate(new Date());  

        // 通过过JobDetail封装HelloJob,同时指定Job在Scheduler中所属组及名称,这里,组名为group1,而名称为job1。  
        JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();  

        // 创建一个SimpleTrigger实例,指定该Trigger在Scheduler中所属组及名称。  
        // 接着设置调度的时间规则.当前时间运行  
        Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();  

        // 注册并进行调度  
        sched.scheduleJob(job, trigger);  

        // 启动调度器  
        sched.start();  

        try {  
            //当前线程等待65秒  
            Thread.sleep(65L * 1000L);  
        } catch (Exception e) {  

        }  
        //调度器停止运行  
        sched.shutdown(true); 
        log.error("结束运行。。。。");      
    }  

    public static void main(String[] args) throws Exception {  
        SimpleExample example = new SimpleExample();  
        example.run();  
    }  
}  

典型的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>

应用一是常见的java实现方式,没有使用Spring去管理Quartz对象,也反映Quartz可以脱离Spring独立执行。应用二是通过Spring配置文件实现执行实例化类的方法,但是把配置写在XML文件里面非常不方便修改。因此借鉴第二种方法实现Quartz的动态加载任务,同理也可实现增加、删除、暂停、启动、单次执行等功能。

考虑到Quartz调度工厂不需要多例,并且考虑注入的便捷性,在SpringMvc.xml文件里配置:

<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"/>

并将其注入到SchedulerJobService中。

package cn.focus.sh.core.quartz.service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.stereotype.Service;

@Service
public class ScheduleJobService {

    @Autowired
    private Scheduler scheduler;

    private static Map<String,Quartz> QuartzMap = new ConcurrentHashMap<String,Quartz>();

    @Autowired
    SpringUtils utils;

    @Autowired
    private QuartzDao quartzDao;

    @PostConstruct
    public void init(){
        //从数据库初始化任务列表
        List<Quartz> list =  quartzDao.selectAll();

        for(Quartz quartz :list){
            run(quartz);
        }
    }
    public void run(Quartz quartz){
        MethodInvokingJobDetailFactoryBean bean =new MethodInvokingJobDetailFactoryBean();
        try {
            Class<?> loader = Class.forName(quartz.getCLASS());
            bean.setTargetObject(utils.getBean(loader));
            bean.setTargetClass(loader);
            bean.setTargetMethod(quartz.getMETHOD());
            bean.setGroup(quartz.getGROUP());
            bean.setName(quartz.getNAME());
            bean.setConcurrent(false);  
            bean.afterPropertiesSet();

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCRON());

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartz.getNAME(), quartz.getGROUP())
                .withSchedule(scheduleBuilder).build();

            try {
                scheduler.scheduleJob(bean.getObject(), trigger);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }
}

CLASS cn.xxx.xxx.manager.xxxManager
METHOD getCount
CRON 0/10 * * * * ?

目前只实现了动态执行任务,同理可以实现动态暂停、删除、增加、修改等功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章