详解Spring整合Quartz实现动态定时任务

最近项目中需要用到定时任务的功能,虽然spring 也自带了一个轻量级的定时任务实现,但感觉不够灵活,功能也不够强大。在考虑之后,决定整合更为专业的Quartz来实现定时任务功能。

普通定时任务

首先,当然是添加依赖的jar文件,我的项目是maven管理的,以下的我项目的依赖:

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>${mybatis.version}</version>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.7.4</version>
  </dependency>
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>${mybatis.spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j.version}</version>
  </dependency>
  <dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>${commons.lang.version}</version>
  </dependency>
  <dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>${commons.dbcp.version}</version>
  </dependency>
  <dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc14</artifactId>
    <version>${ojdbc.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
  </dependency>
  <dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
  </dependency>
</dependencies>

或许你应该看出来了,我的项目是spring整合了mybatis,目前spring的最新版本已经到了4.x系列,但是最新版的mybatis-spring的整合插件所依赖推荐的依然是spring 3.1.3.RELEASE,所以这里没有用spring的最新版而是用了推荐的3.1.3.RELEASE,毕竟最新版本的功能一般情况下也用不到。

至于quartz,则是用了目前的最新版2.2.1

之所以在这里特别对版本作一下说明,是因为spring和quartz的整合对版本是有要求的。

spring3.1以下的版本必须使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然会出错。

至于原因,则是spring对于quartz的支持实现,org.springframework.scheduling.quartz.CronTriggerBean继承了org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是个类,而在quartz2.x系列中org.quartz.CronTrigger变成了接口,从而造成无法用spring的方式配置quartz的触发器(trigger)。

在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法可以是普通类。很显然,第二种方式远比第一种方式来的灵活。

这里采用的就是第二种方式。

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):

//@Component
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表达式备忘:

字段 允许值 允许的特殊字符

  1. 秒 0-59 , – * /
  2. 分 0-59 , – * /
  3. 小时 0-23 , – * /
  4. 日期 1-31 , – * ? / L W C
  5. 月份 1-12 或者 JAN-DEC , – * /
  6. 星期 1-7 或者 SUN-SAT , – * ? / L C #
  7. 年(可选) 留空, 1970-2099 , – * /

表达式意义

"0 0 12 * * ?"       每天中午12点触发
"0 15 10 ? * *"       每天上午10:15触发
"0 15 10 * * ?"       每天上午10:15触发
"0 15 10 * * ? *"      每天上午10:15触发
"0 15 10 * * ? 2005"    2005年的每天上午10:15触发
"0 * 14 * * ?"       在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?"      在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?"     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?"      在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED"    每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI"    周一至周五的上午10:15触发
"0 15 10 15 * ?"      每月15日上午10:15触发
"0 15 10 L * ?"       每月最后一日的上午10:15触发
"0 15 10 ? * 6L"      每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3"      每月的第三个星期五上午10:15触发
0 6 * * *          每天早上6点
0 */2 * * *         每两个小时
0 23-7/2,8 * * *      晚上11点到早上8点之间每两个小时,早上八点
0 11 4 * 1-3        每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点
0 4 1 1 *          1月1日早上4点

动态添加定时任务

前面,我们已经对Spring和Quartz用配置文件的方式进行了整合,如果需求比较简单的话应该已经可以满足了。但是很多时候,我们常常会遇到需要动态的添加或修改任务,而spring中所提供的定时任务组件却只能够通过修改xml中trigger的配置才能控制定时任务的时间以及任务的启用或停止,这在带给我们方便的同时也失去了动态配置任务的灵活性。我搜索了一些网上的解决方法,都没有很好的解决这个问题,而且大多数提到的解决方案都停留在Quartz 1.x系列版本上,所用到的代码和API已经不能适用于新版本的Spring和Quartz。没办法只能靠自己了,花了点时间好好研究了一下Spring和Quartz中相关的代码。

首先我们来回顾一下spring中使用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>

所有的配置都在xml中完成,包括cronExpression表达式,十分的方便。但是如果我的任务信息是保存在数据库的,想要动态的初始化,而且任务较多的时候不是得有一大堆的xml配置?或者说我要修改一下trigger的表达式,使原来5秒运行一次的任务变成10秒运行一次,这时问题就来了,试过在配置文件中不传入cronExpression等参数,但是启动时就报错了,难道我每次都修改xml文件然后重启应用吗,这显然不合适的。最理想的是在与spring整合的同时又能实现动态任务的添加、删除及修改配置。

我们来看一下spring实现quartz的方式,先看一下上面配置文件中定义的jobDetail。其实上面生成的jobDetail并不是我们定义的Bean,因为在Quartz 2.x版本中JobDetail已经是一个接口(当然以前的版本也并非直接生成JobDetail):

public interface JobDetail extends Serializable, Cloneable {…} 

Spring是通过将其转换为MethodInvokingJob或StatefulMethodInvokingJob类型来实现的,这两个都是静态的内部类,MethodInvokingJob类继承于QuartzJobBean,而StatefulMethodInvokingJob则直接继承于MethodInvokingJob。 这两个类的实现区别在于有状态和无状态,对应于quartz的Job和StatefulJob,具体可以查看quartz文档,这里不再赘述。先来看一下它们实现的QuartzJobBean的主要代码:

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

上面主要看我们目前用的Quartz 2.0版本的实现部分,到这里或许你已经明白Spring对Quartz的封装原理了。Spring就是通过这种方式在最后Job真正执行时反调用到我们所注入的类和方法。

现在,理解了Spring的实现原理后,我们就可以来设计我们自己的了。在设计时我想到以下几点:

1、减少spring的配置文件,为了实现一个定时任务,spring的配置代码太多了。

2、用户可以通过页面等方式添加、启用、禁用某个任务。

3、用户可以修改某个已经在运行任务的运行时间表达式,CronExpression。

4、为方便维护,简化任务的运行调用处理,任务的运行入口即Job实现类最好只有一个,该Job运行类相当于工厂类,在实际调用时把任务的相关信息通过参数方式传入,由该工厂类根据任务信息来具体执行需要的操作。

在上面的思路下来进行我们的开发吧。

一、spring配置文件

通过研究,发现要实现我们的功能,只需要以下配置:

二、任务运行入口,即Job实现类,在这里我把它看作工厂类:

/**
 * 定时任务运行工厂类
 *
 * @author tq
 * @date 2016/5/1
 */
public class QuartzJobFactory 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() + "]");
  }
}

这里我们实现的是无状态的Job,如果要实现有状态的Job在以前是实现StatefulJob接口,在我使用的quartz 2.2.1中,StatefulJob接口已经不推荐使用了,换成了注解的方式,只需要给你实现的Job类加上注解@DisallowConcurrentExecution即可实现有状态:

/**
* 定时任务运行工厂类
 * @author tq
 * @date 2016/5/1
*/
@DisallowConcurrentExecution
public class QuartzJobFactory implements Job {...}

三、创建任务

既然要动态的创建任务,我们的任务信息当然要保存在某个地方了,这里我们新建一个保存任务信息对应的实体类:

/**
 * 计划任务信息
 *
 * @author tq
 * @date 2016/5/1
 */
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;
  getter and setter ....
}

接下来我们创建测试数据,实际应用中该数据可以保存在数据库等地方,我们把任务的分组名+任务名作为任务的唯一key,和quartz中的实现方式一致:

/** 计划任务map */
private static Map<String, ScheduleJob> jobMap = new HashMap<String, ScheduleJob>();

static {
  for (int i = 0; i < 5; i++) {
    ScheduleJob job = new ScheduleJob();
    job.setJobId("10001" + i);
    job.setJobName("data_import" + i);
    job.setJobGroup("dataWork");
    job.setJobStatus("1");
    job.setCronExpression("0/5 * * * * ?");
    job.setDesc("数据导入任务");
    addJob(job);
  }
}

/**
 * 添加任务
 * @param scheduleJob
 */
public static void addJob(ScheduleJob scheduleJob) {
  jobMap.put(scheduleJob.getJobGroup() + "_" + scheduleJob.getJobName(), scheduleJob);
}

有了调度工厂,有了任务运行入口实现类,有了任务信息,接下来就是创建我们的定时任务了,在这里我把它设计成一个Job对应一个trigger,两者的分组及名称相同,方便管理,条理也比较清晰,在创建任务时如果不存在新建一个,如果已经存在则更新任务,主要代码如下:

//schedulerFactoryBean 由spring创建注入
Scheduler scheduler = schedulerFactoryBean.getScheduler();

//这里获取任务信息数据
List<ScheduleJob> jobList = DataWorkContext.getAllJob();

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(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);
  } else {
    // Trigger已存在,那么更新相应的定时设置
    //表达式调度构建器
    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
      .getCronExpression());

    //按新的cronExpression表达式重新构建trigger
    trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
      .withSchedule(scheduleBuilder).build();

    //按新的trigger重新设置job执行
    scheduler.rescheduleJob(triggerKey, trigger);
  }
}

如此,可以说已经完成了我们的动态任务创建,大功告成了。有了上面的代码,添加和修改任务是不是也会了,顺道解决了?

上面我们创建的5个测试任务,都是5秒执行一次,都将调用QuartzJobFactory的execute方法,但是传入的任务信息参数不同,execute方法中的如下代码就是得到具体的任务信息,包括任务分组和任务名:

代码如下:

ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get(“scheduleJob”);

有了任务分组和任务名即确定了该任务的唯一性,接下来需要什么操作实现起来是不是就很容易了?

以后需要添加新的定时任务只需要在任务信息列表中加入记录即可,然后在execute方法中通过判断任务分组和任务名来实现你具体的操作。

以上已经初始实现了我们需要的功能,增加和修改也已经可以通过源代码举一反三出来,但是我们在实际开发的时候需要进行测试,如果一个任务是1个小时运行一次的,测试起来是不是很不方便?当然你可以修改任务的运行时间表达式,但相信这不是最好的方法,接下来我们就要实现在不对当前任务信息做任何修改的情况下触发任务,并且该触发只会运行一次作测试用。

动态暂停 恢复 修改和删除任务

前面我们已经完成了spring 3和quartz 2的整合以及动态添加定时任务,我们接着来完善它,使之能支持更多的操作,例如暂停、恢复、修改等。

在动态添加定时任务中其实已经涉及到了其中的一些代码,这里我们再来细化的理一理。先来看一下我们初步要实现的目标效果图,这里我们只在内存中操作,并没有把quartz的任何信息保存到数据库,即使用的是RAMJobStore,当然如果你有需要,可以实现成JDBCJobStore,那样任务信息将会更全面。

trigger各状态说明:

  1. None:Trigger已经完成,且不会在执行,或者找不到该触发器,或者Trigger已经被删除
  2. NORMAL:正常状态
  3. PAUSED:暂停状态
  4. COMPLETE:触发器完成,但是任务可能还正在执行中
  5. BLOCKED:线程阻塞状态
  6. ERROR:出现错误

计划中的任务

指那些已经添加到quartz调度器的任务,因为quartz并没有直接提供这样的查询接口,所以我们需要结合JobKey和Trigger来实现,核心代码:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
for (JobKey jobKey : jobKeys) {
  List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
  for (Trigger trigger : triggers) {
    ScheduleJob job = new ScheduleJob();
    job.setJobName(jobKey.getName());
    job.setJobGroup(jobKey.getGroup());
    job.setDesc("触发器:" + trigger.getKey());
    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
    job.setJobStatus(triggerState.name());
    if (trigger instanceof CronTrigger) {
      CronTrigger cronTrigger = (CronTrigger) trigger;
      String cronExpression = cronTrigger.getCronExpression();
      job.setCronExpression(cronExpression);
    }
    jobList.add(job);
  }
}

上面代码中的jobList就是我们需要的计划中的任务列表,需要注意一个job可能会有多个trigger的情况,在下面讲到的立即运行一次任务的时候,会生成一个临时的trigger也会出现在这。这里把一个Job有多个trigger的情况看成是多个任务。我们前面包括在实际项目中一般用到的都是CronTrigger ,所以这里我们着重处理了下CronTrigger的情况。

运行中的任务

实现和计划中的任务类似,核心代码:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
  ScheduleJob job = new ScheduleJob();
  JobDetail jobDetail = executingJob.getJobDetail();
  JobKey jobKey = jobDetail.getKey();
  Trigger trigger = executingJob.getTrigger();
  job.setJobName(jobKey.getName());
  job.setJobGroup(jobKey.getGroup());
  job.setDesc("触发器:" + trigger.getKey());
  Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
  job.setJobStatus(triggerState.name());
  if (trigger instanceof CronTrigger) {
    CronTrigger cronTrigger = (CronTrigger) trigger;
    String cronExpression = cronTrigger.getCronExpression();
    job.setCronExpression(cronExpression);
  }
  jobList.add(job);
}

暂停任务

这个比较简单,核心代码:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.pauseJob(jobKey);

恢复任务

和暂停任务相对,核心代码:

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.resumeJob(jobKey);

删除任务

删除任务后,所对应的trigger也将被删除

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.deleteJob(jobKey);

立即运行任务

这里的立即运行,只会运行一次,方便测试时用。quartz是通过临时生成一个trigger的方式来实现的,这个trigger将在本次任务运行完成之后自动删除。trigger的key是随机生成的,例如:DEFAULT.MT_4k9fd10jcn9mg。在我的测试中,前面的DEFAULT.MT是固定的,后面部分才随机生成。

Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
scheduler.triggerJob(jobKey);

更新任务的时间表达式

更新之后,任务将立即按新的时间表达式执行:

Scheduler scheduler = schedulerFactoryBean.getScheduler();

TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(),
  scheduleJob.getJobGroup());

//获取trigger,即在spring配置文件中定义的 bean id="myTrigger"
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

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

//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
  .withSchedule(scheduleBuilder).build();

//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解spring多线程与定时任务

    本篇主要描述一下spring的多线程的使用与定时任务的使用. 1.spring多线程任务的使用 spring通过任务执行器TaskExecutor来实现多线程与并发编程.通常使用ThreadPoolTaskExecutor来实现一个基于线程池的TaskExecutor. 首先你要实现AsyncConfigurer 这个接口,目的是开启一个线程池 代码如下: package com.foreveross.service.weixin.test.thread; import java.util.co

  • spring 定时任务@Scheduled详解

    一.配置文件 <?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:task="http://www.springframework.org

  • Spring实现动态修改时间参数并手动开启关停操作

    spring实现定时任务的方式有三种,分别是java自带的timer类.spring task和quartz三种. 本文只介绍spring自带的task和第三方quartz.spirng task可以将它比作一个轻量级的Quartz,使用起来非常简单,除spring相关的包外不需要额外的包,而且支持注解和配置文件两种.接着直接演示spring task注解实现方式. 首先,创建任务类,如下: @Component public class MyScheduler { int i =0; publ

  • 详解Spring Boot中使用@Scheduled创建定时任务

    我们在编写Spring Boot应用中经常会遇到这样的场景,比如:我需要定时地发送一些短信.邮件之类的操作,也可能会定时地检查和监控一些标志.参数等. 创建定时任务 在Spring Boot中编写定时任务是非常简单的事,下面通过实例介绍如何在Spring Boot中创建定时任务,实现每过5秒输出一下当前时间. 在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置 @SpringBootApplication @EnableScheduling publi

  • Spring Task定时任务的配置和使用详解

    记录下Spring自带的定时任务用法. spring中使用定时任务 基于xml配置文件使用定时任务 首先配置spring开启定时任务 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/

  • SpringBoot定时任务两种(Spring Schedule 与 Quartz 整合 )实现方法

    前言 最近在项目中使用到定时任务,之前一直都是使用Quartz 来实现,最近看Spring 基础发现其实Spring 提供 Spring Schedule 可以帮助我们实现简单的定时任务功能. 下面说一下两种方式在Spring Boot 项目中的使用. Spring Schedule 实现定时任务 Spring Schedule 实现定时任务有两种方式 1. 使用XML配置定时任务, 2. 使用 @Scheduled 注解. 因为是Spring Boot 项目 可能尽量避免使用XML配置的形式,

  • spring中定时任务taskScheduler的详细介绍

    前言 众所周知在spring 3.0版本后,自带了一个定时任务工具,而且使用简单方便,不用配置文件,可以动态改变执行状态.也可以使用cron表达式设置定时任务. 被执行的类要实现Runnable接口 TaskScheduler接口 TaskScheduler是一个接口,TaskScheduler接口下定义了6个方法 1.schedule(Runnable task, Trigger trigger); 指定一个触发器执行定时任务.可以使用CronTrigger来指定Cron表达式,执行定时任务

  • 详解Spring整合Quartz实现动态定时任务

    最近项目中需要用到定时任务的功能,虽然spring 也自带了一个轻量级的定时任务实现,但感觉不够灵活,功能也不够强大.在考虑之后,决定整合更为专业的Quartz来实现定时任务功能. 普通定时任务 首先,当然是添加依赖的jar文件,我的项目是maven管理的,以下的我项目的依赖: <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring

  • Spring整合Quartz实现动态定时器的示例代码

    一.版本说明 spring3.1以下的版本必须使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然会出错. 原因:spring对于quartz的支持实现,org.springframework.scheduling.quartz.CronTriggerBean继承了org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是个类,而在quartz2.x系列中org.quartz.CronTrigger变成了接口,从

  • 详解Spring Boot + Mybatis 实现动态数据源

    动态数据源 在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库.又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据源方案进行解决.接下来,我们就来讲解如何实现动态数据源,以及在过程中剖析动态数据源背后的实现原理. 实现案例 本教程案例基于 Spring Boot + Mybatis + MySQL 实现. 数据库设计 首先需要安装好MySQL数据库,新建数据库 example,创建example表,用来测

  • 详解Spring整合Ehcache管理缓存

    前言 Ehcache 是一个成熟的缓存框架,你可以直接使用它来管理你的缓存. Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现.它支持注解方式使用缓存,非常方便. 本文先通过Ehcache独立应用的范例来介绍它的基本使用方法,然后再介绍与Spring整合的方法. 概述 Ehcache是什么? EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点.它是Hibernate中的默认缓存框架. Ehcache已经发布

  • 详解spring整合hibernate的方法

    结构: Spring和Hibernate整合借助于HibernateTemplate applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-ins

  • 详解spring整合shiro权限管理与数据库设计

    之前的文章中我们完成了基础框架的搭建,现在基本上所有的后台系统都逃不过权限管理这一块,这算是一个刚需了.现在我们来集成shiro来达到颗粒化权限管理,也就是从连接菜单到页面功能按钮,都进行权限都验证,从前端按钮的显示隐藏,到后台具体功能方法的权限验证. 首先要先设计好我们的数据库,先来看一张比较粗糙的数据库设计图: 具体的数据库设计代码 /* Navicat MySQL Data Transfer Source Server : 本机 Source Server Version : 50537

  • 详解Spring中实现接口动态的解决方法

    前言 本文主要给大家介绍的是关于Spring实现接口动态的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 关于这个问题是因为领导最近跟我提了一个需求,是有关于实现类Mybatis的@Select.@Insert注解的功能.其是基于interface层面,不存在任何的接口实现类.因而在实现的过程中,首先要解决的是如何动态实现接口的实例化.其次是如何将使接口根据注解实现相应的功能. 声明 解决方案是基于Mybatis源码,进行二次开发实现. 解决方法 我们先来看看Mybat

  • spring整合Quartz框架过程详解

    这篇文章主要介绍了spring整合Quartz框架过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.Quartz框架简介 Quartz是一个完全由Java编写的开源任务调度的框架,通过触发器设置作业定时运行规则,控制作业的运行时间.其中quartz集群通过故障切换和负载平衡的功能,能给调度器带来高可用性和伸缩性.主要用来执行定时任务,如:定时发送信息.定时生成报表等等. Quartz框架的主要特点: · 强大的调度功能,例如丰富多样的

  • 详解Spring如何整合Mybatis

    第一步 导入相关jar包 <dependencies> <!--连接mysql--> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <versi

  • 详解spring cloud config整合gitlab搭建分布式的配置中心

    在前面的博客中,我们都是将配置文件放在各自的服务中,但是这样做有一个缺点,一旦配置修改了,那么我们就必须停机,然后修改配置文件后再进行上线,服务少的话,这样做还无可厚非,但是如果是成百上千的服务了,这个时候,就需要用到分布式的配置管理了.而spring cloud config正是用来解决这个问题而生的.下面就结合gitlab来实现分布式配置中心的搭建.spring cloud config配置中心由server端和client端组成, 前提:在gitlab中的工程下新建一个配置文件config

随机推荐