转载:spring4.0 整合 Quartz 实现动态任务调度

项目ITP(六) spring4.0 整合 Quartz 实现动态任务调度 - Jeff Li

系列文章: [传送门]

项目需求:

http://www.cnblogs.com/Alandre/p/3733249.html

上一博客写的是基本调度,后来这只能用于,像每天定个时间 进行数据库备份。但是,远远不能在上次的需求上实现。所以需要实现spring4.0 整合 Quartz 实现动态任务调度。

正文 

spring4.0 整合 Quartz 实现 任务调度 。这真是期末项目的最后一篇,剩下到暑假吧。

   Quartz 介绍

  Quartz is a full-featured, open source job scheduling service that can be integrated with, or used along side virtually any Java application - from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs; 

  Quartz框架是一个全功能、开源的任务调度服务,可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度。

   核心概念

  Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节

相关文档: http://www.quartz-scheduler.org/documentation/quartz-2.2.x/quick-start

回顾

上次我们配置了

<!--Quartz-->
      
  <!-- 集成方式:JobDetailFactoryBean,并且任务类需要继承QuartzJobBean-->
  <!-- 定义jobDetail -->
  <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <!-- durability 表示任务完成之后是否依然保留到数据库,默认false   -->
    <property name="durability" value="true" />  
    <!--	 目标类  /wmuitp/src/test/SpringQuartz.java-->
    <property name="jobClass" value="test.SpringQuartzTest"></property>
    
       <!--  在这个例子中,jobDataAsMap没有用,此目标类中接受的参数 ,若参数为service,则可以在此进行参数配置,类似struts2 -->
       <!--
    <property name="jobDataAsMap">  
      <map>  
        <entry key="service"><value>simple is the beat</value></entry>  
      </map>  
    </property>
      -->
  </bean>
  
  <!-- 定义simpleTrigger触发器 -->
  <!--	 
  <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
    <property name="jobDetail" ref="jobDetail"></property>
    <property name="repeatCount">
      <value>8</value>
    </property>
    <property name="repeatInterval">
      <value>1000</value>
    </property>
    <property name="startDelay">
      <value>4</value>
    </property>
  </bean> 
  -->
  
  <!-- 另一种触发器是CornTrigger -->
   <bean id="cornTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
     <property name="jobDetail" ref="jobDetail"/>
     <!-- 每个10秒触发 -->
     <property name="cronExpression" value="0/10 * * * * ?"/>
  </bean> 
  
  <!-- 定义核心调度器 -->
  <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
    <ref bean="cornTrigger"/>
    </property>
  </bean>

#spring实现quartz的方式,先看一下上面配置文件中定义的jobDetail。在Quartz 2.x版本中JobDetail已经是一个接口, Spring是通过将其转换为MethodInvokingJob或StatefulMethodInvokingJob类型来实现的。

这是文档中的源码:

/**
 * 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);
}

#既然知道了其所以然,我们就可以真正实战了。

实战 

听我慢慢道来

减少spring的配置文件

为了实现一个定时任务,spring的配置代码太多了。动态配置需要们手动来搞。这里我们只需要这要配置即可:

    <!-- quartz配置  动态配置所以我们将 Factory 作为一个service一样的接口 QuartzJobFactory.java-->
    <!-- 调度工厂 -->
    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    </bean>

Job实现类

在这里我把它看作工厂类:

package test;

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

@DisallowConcurrentExecution
public class QuartzJobFactoryImpl 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() + "]");
  }

}

任务对应实体类

package test;

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 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;
  }
  
  
}

下面我们就来测试下:

Controller 测试代码:

  @RequestMapping(value = "/quartz")
  public ModelAndView quartz() throws SchedulerException 
  {	
    
    //schedulerFactoryBean 由spring创建注入
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");  
    System.out.println(ctx);
    Scheduler scheduler = (Scheduler)ctx.getBean("schedulerFactoryBean");
    
    System.out.println(scheduler);
    //这里获取任务信息数据
    List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
    
    for (int i = 0; i < 3; i++) {
      ScheduleJob job = new ScheduleJob();
      job.setJobId("10001" + i);
      job.setJobName("JobName_" + i);
      job.setJobGroup("dataWork");
      job.setJobStatus("1");
      job.setCronExpression("0/5 * * * * ?");
      job.setDesc("数据导入任务");
      jobList.add(job);
    }

    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(QuartzJobFactoryImpl.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);
      }
    }
    
    ModelAndView mav = new ModelAndView(AdminWebConstant.ADMIN_LOGIN_VIEW);
    return mav;
  }

 #后面这块应该会进一步整理。到时候 会出个更详细的。期待吧

测试结果:

总结

spring quartz

感谢及资源共享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值