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

来源:互联网 发布:李子树下埋死人 知乎 编辑:程序博客网 时间:2024/06/06 16:46

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;@Servicepublic 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 * * * * ?

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

0 0
原创粉丝点击