Spring 3整合Quartz 2实现定时任务(转)

http://www.meiriyouke.net/?p=82

最近工作中需要用到定时任务的功能,虽然Spring3也自带了一个轻量级的定时任务实现,但感觉不够灵活,功能也不够强大。在考虑之后,决定整合更为专业的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表达式备忘:

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

秒 0-59 , – * /

分 0-59 , – * /

小时 0-23 , – * /

日期 1-31 , – * ? / L W C

月份 1-12 或者 JAN-DEC , – * /

星期 1-7 或者 SUN-SAT , – * ? / L C #

年(可选) 留空, 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触发

每天早上6点

0 6 * * *

每两个小时

0 */2 * * *

晚上11点到早上8点之间每两个小时,早上八点

0 23-7/2,8 * * *

每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点

0 11 4 * 1-3

1月1日早上4点

0 4 1 1 *

动态添加任务

http://www.meiriyouke.net/?p=131

前面,我们已经对Spring 3和Quartz 2用配置文件的方式进行了整合,如果需求比较简单的话应该已经可以满足了。但是很多时候,我们常常会遇到需要动态的添加或修改任务,而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):

  1. 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配置文件

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

  1. <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />
二、任务运行入口,即Job实现类,在这里我把它看作工厂类:
复制代码
/*** 定时任务运行工厂类* * User: liyd* Date: 14-1-3* Time: 上午10:11*/
public class QuartzJobFactory implements Job {@Overridepublic 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即可实现有状态:

  1. /**
  2. * 定时任务运行工厂类
  3. * <p/>
  4. * User: liyd
  5. * Date: 14-1-3
  6. * Time: 上午10:11
  7. */
  8. @DisallowConcurrentExecution
  9. public class QuartzJobFactory implements Job {...}
三、创建任务

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

复制代码
/*** 计划任务信息* * User: liyd* Date: 14-1-3* Time: 上午10:24*/
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表达式构建一个新的triggertrigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();scheduler.scheduleJob(jobDetail, trigger);} else {// Trigger已存在,那么更新相应的定时设置//表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());//按新的cronExpression表达式重新构建triggertrigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();//按新的trigger重新设置job执行scheduler.rescheduleJob(triggerKey, trigger);}
}
复制代码

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

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

  1. ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");

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

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

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

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

http://www.meiriyouke.net/?p=140

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

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

如上图,我们要先列出计划中的定时任务以及正在执行中的定时任务,这里的正在执行中指的是任务已经触发线程还没执行完的情况。比如每天2点执行一个数据导入操作,这个操作执行时间需要5分钟,在这5分钟之内这个任务才是运行中的任务。当任务正常时可以使用暂停按钮,任务暂停时可以使用恢复按钮。

trigger各状态说明:

None:Trigger已经完成,且不会在执行,或者找不到该触发器,或者Trigger已经被删除
NORMAL:正常状态
PAUSED:暂停状态
COMPLETE:触发器完成,但是任务可能还正在执行中
BLOCKED:线程阻塞状态
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);
}
复制代码
暂停任务

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

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. scheduler.pauseJob(jobKey);
恢复任务

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

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. scheduler.resumeJob(jobKey);
删除任务

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

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. scheduler.deleteJob(jobKey);
立即运行任务

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

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. 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);
复制代码

到这里,我们的spring3 整合quartz 2的定时任务功能终于是告一段落了,对常用的一些功能进行了实现,相信可以满足一般项目的需求了

http://www.cnblogs.com/davidwang456/p/3858980.html

转载于:https://www.cnblogs.com/softidea/p/4939113.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/262541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 编译之后黑屏_抖音BoostMultiDex:Android低版本上首次启动时间减少80%(一)...

我们知道&#xff0c;Android 低版本(4.X 及以下&#xff0c;SDK < 21)的设备&#xff0c;采用的 Java 运行环境是 Dalvik 虚拟机。它相比于高版本&#xff0c;最大的问题就是在安装或者升级更新之后&#xff0c;首次冷启动的耗时漫长。这常常需要花费几十秒甚至几分钟&…

[转]关于多线程并发:每个开发人员都应了解的内容(转自Mainz's)

Concurrency: What Every Dev Must Know About Multithreaded Apps 本文讨论: 多线程和共享内存线程模型争用及并发访问如何能够打破不变量作为争用标准解决方案的锁定何时需要锁定如何使用锁定&#xff1b;理解开销锁定如何能够各行其道十年前&#xff0c;只有核心系统程序员会…

Filter案例之登录验证

一、登录验证&#xff0c;权限控制 1、需求分析 其中&#xff0c;登录有关的资源被访问时要直接放行&#xff0c;不然会死循环&#xff1b; 2、代码实现 转载于:https://www.cnblogs.com/wmqiang/p/11604621.html

js导出excel单元格内换行符代码_前端和excel的那些事

前端和 excel 的那点事在开发需求中&#xff0c;经常会遇到需要和excel相关的需求&#xff0c;毕竟不是所有人都是程序员&#xff0c;很多的业务都是通过excel去进行数据的整理归类计算的&#xff0c;excel中提供的一系列快捷功能&#xff0c;统计功能也非常的实用&#xff0c;…

Filter案例之敏感词过滤和代理模式

一、需求分析 二 、代理模式 1、概念 2、代码实现 代理对象可以强转为真实对象&#xff0c;即对应的接口类&#xff1b; 3、通过代理增强方法 其中&#xff0c;方法对象invoke真实对象&#xff0c;反射原理&#xff1b; 三、过滤敏感词汇案例代码实现 本地的字符流和字节流创建…

555定时器回差电压计算公式_555时基电路引脚解析

555时基电路引脚解析凡是时基电路555&#xff0c;电路内部结构相同&#xff0c;性能都是相同的。 时基电路555有很多厂家型号&#xff0c;如MC555、CA555、XR555、LM555等&#xff1b;国产型号有SL555、FX555、5G1555等&#xff0c;典型的、也是最常用的是NE555。555前的字母只…

麻省理工学院(MIT)的公开课程

很早之前就听说麻省理工学院把很多课程公开了&#xff0c;今天偶尔搜到这个内容。还不错 http://www.core.org.cn/OcwWeb/index.htm 课程列表 http://www.core.org.cn/OcwWeb/Global/all-courses.htm转载于:https://www.cnblogs.com/chenxizhang/archive/2009/04/27/1444255.ht…

66319d电源使用说明书_矿用防爆交换机如何正确安装调试和使用?

矿用防爆交换机是矿山的通讯核心&#xff0c;它不同于普通交换机&#xff0c;因应用环境的特殊性&#xff0c;在安装以及后续使用过程中有更多要求。在使用过程中&#xff0c;出保障交换机本身的部件无损、性能不受影响外&#xff0c;还要注意使用环境&#xff0c;保证现场安全…

xcode7.1 安装不了Alcatraz怎么办.看这里

按照github上面的安装的方法,但是发现并没有成功.这是为什么呢? 解决的方法是什么呢? 看下面: 这个时候新的问题来了,要怎么获取7.1的UUIDs呢?看下面:   打开你的终端,输入下面的代码就可以了: defaults read /Applications/Xcode.app/Contents/Info DVTPlugInCompatibili…

epub 机器人系列 阿西莫夫_艾萨克·阿西莫夫是不是穿越来的,他怎么能那么准地预言机器人,互联网技术呢?...

机器人&#xff0c;纳米技术&#xff0c;网络图书馆&#xff0c;视频电话&#xff0c;当这些悄悄走进我们的生活&#xff0c;如果有人问你&#xff0c;是什么改变了我们的生活&#xff1f;你可能会回答&#xff0c;当然是科技的发展。如果再问你&#xff0c;这些科技的发展最初…

js怎么调用wasm_Long.js源码解析

基于现在市面上到处都是 Vue/React 之类的源码分析文章实在是太多了。(虽然我也写过 Vite的源码解析所以这次来写点不一样的。由于微信这边用的是 protobuf 来进行 rpc 调用。所以有时候需要将 JS 中的 Number 类型转换为 Long 类型传给后端。目前用的最多的就是 Long.js 了&am…

AOP快速入门

一、概念 AOP面向切面编程&#xff0c;是函数式编程的延申&#xff0c;是对OOP的补充&#xff1b; 代理模式&#xff1a;拦截增强作用&#xff0c;增强功能&#xff1b; 1、java继承&#xff0c;纵向共性抽取&#xff0c; 2、横向切面AOP织入增强代码方式 二、原理是通过代理机…

图形大小_PS图形复制——等距复制、旋转复制、大小变换复制

PS中图形复制是很常见的事&#xff0c;可能不同人的习惯可能会用不同的方法。这里糖糖主要讲的是图形的一些等距复制、旋转复制、大小变换旋转复制&#xff0c;相信看过本篇文章之后你也可以通过图形的不同需要的复制做一些很不错的图案哦~糖糖先在PS里用钢笔工具绘制了一个绿叶…

AOP联盟通知类型和Spring编写代理半自动

一、cglib功能更强大 二、Spring核心jar包 三、AOP联盟通知 三、代码实现Spring半自动代理 1、环绕通知的切面 2、bean.xml配置 3、创建bean容器&#xff0c;获取bean&#xff0c;即已经创建好的代理对象&#xff1a; 4、配置多个接口 转载于:https://www.cnblogs.com/wmqiang/…

1到30图片大全顺序_终于解决了!自媒体作者,如何给 Word 中的图片批量编号?...

今天这个技巧非常有用&#xff0c;特别是对小编这种每天都要写图文教程的人来说&#xff0c;简直是续命神技。如果 Word 中有大量的图片&#xff0c;经常需要个每个图片顺序编号&#xff0c;有没有同款需求&#xff1f;大家平时都一个个手工输入的吗&#xff1f;编到后来很可能…

Android Volley框架的使用(二)

此博文源码下载地址 https://github.com/Javen205/VolleyDemo.git 使用请求队列RequestQueue Volley中的Request都需要添加到RequestQueue中才能执行&#xff0c;所以首先需要创建一个RequestQueue RequestQueue Volley.newRequestQueue(mContext); 通常情况在一个应用中需要…

Spring全自动AOP和项目加入jar包

一、jar可以引进项目中&#xff0c;复制到路下后&#xff0c;要add as library&#xff0c;加载到工作空间中才能引入&#xff1b; 也jar包放在硬盘的项目目录外面&#xff0c;可以多个项目引入共用&#xff1a; 二、xml配置 1、aop全自动配置 2、xml装配bean,不是注解方式 3、…

中怎样载入选区_ps中快捷大全

使用ps时使用快捷键更方便&#xff0c;操作起来更能提高工作效率&#xff0c;下面给大家总结一下&#xff0c;希望对大家有帮助&#xff0c;当然大家有更好的技巧可以来魔课66网一起交流一下文件设置:Ctrln——新建Ctrlo——打开Ctrlp——打印Ctrls—— 存储Ctrlw——关闭Ctrl …

AspectJ——AOP框架快速入门

一、导包 二、bean.xml配置 三、环绕通知 四&#xff0c;表达式 转载于:https://www.cnblogs.com/wmqiang/p/11617042.html

linux桌面环境应用

为什么80%的码农都做不了架构师&#xff1f;>>> 通常的 Linux 发行版都使用 KDE 或者 GNOME 作为默认的桌面环境。它们都给用户提供了一个原始的并且有吸引力的桌面&#xff0c;并且内置了各式各样的多媒体软件、系统程序、游戏、实用程序、网页开发工具、编程 工具…