在开发时我们会常常遇到定时任务可以由客户进行管理在什么时候去执行或者甚至不再执行该定时任务。而Spring中所提供的定时任务组件却只能够通过修改trigger的配置才能够控制定时的时间以及是否启用定时任务,为此我搜索了网上的一些解决方法,发现还是不能够很好的解决这个问题。所以干脆仔仔细细的研究了一把Quartz和Spring中相关的源码,我们发现当我们在Spring通过如下声明定时任务时:
<bean id="yourJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="yourJobBean"/>
<property name="targetMethod" value="yourJobMethod"/>
<property name="concurrent" value="false"/>
</bean>
<bean id="yourCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >
<property name="jobDetail" ref="yourobDetail"/>
<property name="cronExpression">
<value>0 0 2 * * ?</value>
</property>
</bean>
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="yourCronTrigger"/>
</list>
</property>
</bean>
所生成的Quartz的JobDetail并不是你定义的Bean,因为JobDetail并不能够直接存放一个实例,具体可以参考JobDetail的构造函数:
JobDetail(String name, String group, Class jobClass)
Create a JobDetail with the given name, group and class, and the default settings of all the other properties.
Spring将其转换为MethodInvokingJob或者StatefulMethodInvokingJob类型,其实这两个类都是从QuartzJobBean类继承而来,那么我们来看看QuartzJobBean类的代码:
public abstract class QuartzJobBean implements Job {
/**
* 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 {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValues(context.getScheduler().getContext());
pvs.addPropertyValues(context.getMergedJobDataMap());
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;
}
看到这个你或许已经明白了Spring对Quartz的封装原理了。是的,Spring通过这种方式最后就可以在Job真正执行的时候可以反调用到我们所注入的类和方法,具体的代码在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 ? (Class) MethodInvokingJob.class : StatefulMethodInvokingJob.class);
// Build JobDetail instance.
this.jobDetail = new JobDetail(name, this.group, jobClass);
this.jobDetail.getJobDataMap().put("methodInvoker", this);
this.jobDetail.setVolatility(true);
this.jobDetail.setDurability(true);
// Register job listener names.
if (this.jobListenerNames != null) {
for (int i = 0; i < this.jobListenerNames.length; i++) {
this.jobDetail.addJobListener(this.jobListenerNames[i]);
}
}
postProcessJobDetail(this.jobDetail);
}
其实主要是这句:
this.jobDetail.getJobDataMap().put("methodInvoker", this);
这样在调用的时候那就可以调用到我们所注入的类和方法了。
那好我们理解了Spring的原理后就可以动手修改成我们自己的了,所以在设计的时候我们考虑到以下两个方面:
- 精简Spring的配置,添加一个定时任务至少有10几行配置代码,太麻烦了,最好只声明一个Bean就可以了;
- 客户不但能够控制执行的时间,还可以能够启用/禁用某个定时任务。
在上面的思路下,我们决定保留Spring的这个配置:
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
</bean>
只不过不需要注入trigger这个属性了,我们仅仅利用Spring来帮我们构造一个Scheduler。
为了简化后续的处理,我们决定定义一个自己Job的接口,方便进行控制及精简代码。
public interface MyJob {
/**
* 执行具体的任务处理
* @throws JobException
*/
public void execute() throws JobException;
}
接下来我们还是需要一个类似QuartzJobBean类,因此参考Spring的更改如下:
public class QuartzJobBean implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
String targetBeanId = (String)context.getMergedJobDataMap().get("targetObjectId");
if(StringUtils.isNullString(targetBeanId))
return;
Object targetBean = SpringUtils.getBean(targetBeanId);
if(null == targetBean)
return;
// 判断是否是实现了MyJob接口
if(!(targetBean instanceof MyJob))
return;
// 执行相应的任务
((MyJob)targetBean).execute();
}
}
这个类处理逻辑就是通过获取我在创建JobDetail任务是设定的目标任务Bean的,即targetObjectId的值,然后通过SpringUtils获取该Bean,最后转换成MyJob接口执行。
如何创建JobDetail呢,因为我们前面已经说明不需要在Spring配置那么麻烦而且客户还可以进行配置,因此我们将任务的定时信息存放在数据库中,相应的Domain定义如下:
public class SchedulingJob extends Entry {
public static final int JS_ENABLED = 0; // 任务启用状态
public static final int JS_DISABLED = 1; // 任务禁用状态
public static final int JS_DELETE = 2; // 任务已删除状态
private String jobId; // 任务的Id,一般为所定义Bean的ID
private String jobName; // 任务的描述
private String jobGroup; // 任务所属组的名称
private int jobStatus; // 任务的状态,0:启用;1:禁用;2:已删除
private String cronExpression; // 定时任务运行时间表达式
private String memos; // 任务描述
// 省略getter和setter... ...
public String getTriggerName(){
return this.getJobId() + "Trigger";
}
}
这时候我们就可以对定时任务进行控制了,具体控制代码如下:
/**
* 启用指定的定时任务
* @param context
* @param schedulingJob
*/
protected void enabled(Context context, SchedulingJob schedulingJob) {
try {
CronTrigger trigger = (CronTrigger)this.scheduler.getTrigger(schedulingJob.getTriggerName(), schedulingJob.getJobGroup());
if (null == trigger) {
// Trigger不存在,那么创建一个
JobDetail jobDetail = new JobDetail(schedulingJob.getJobId(), schedulingJob.getJobGroup(), QuartzJobBean.class);
jobDetail.getJobDataMap().put("targetObjectId", schedulingJob.getJobId());
trigger = new CronTrigger(schedulingJob.getTriggerName(), schedulingJob.getJobGroup(), schedulingJob.getCronExpression());
this.scheduler.scheduleJob(jobDetail, trigger);
}else{
// Trigger已存在,那么更新相应的定时设置
trigger.setCronExpression(schedulingJob.getCronExpression());
this.scheduler.rescheduleJob(trigger.getName(), trigger.getGroup(), trigger);
}
} catch (SchedulerException e) {
e.printStackTrace();
// TODO
} catch (ParseException e) {
e.printStackTrace();
// TODO
}
}
/**
* 禁用指定的定时任务
* @param context
* @param schedulingJob
*/
protected void disabled(Context context, SchedulingJob schedulingJob) {
try {
Trigger trigger = this.scheduler.getTrigger(schedulingJob.getTriggerName(), schedulingJob.getJobGroup());
if (null != trigger) {
this.scheduler.deleteJob(schedulingJob.getJobId(), schedulingJob.getJobGroup());
}
} catch (SchedulerException e) {
e.printStackTrace();
// TODO
}
}
再加上前台处理页面(这个俺就不写了,估计大家都会) 这样我们就可以在控制定时任务的定时时间及启用和禁用了。
最后我们的配置代码如下:
<bean id="taskScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"/>
<bean id="tempDirClearTask" class="com.my.module.attachment.util.TempDirClearTask"/>
P.S.
这个定时任务组件是有局限性的,比如所设置的SchedulingJob.jobID必须是和Spring中定时任务Bean的id一致,否则这个定时任务不会被运行。还有这个现在仅支持CronTrigger。而且也不支持分组,不过你可以通过扩展。
再一个要记得写一个加载类在系统初始化时从数据库的配置中加载所有启用的定时任务哦,不然所有定时任务都不能够运行
分享到:
相关推荐
在Spring中整合Quartz,我们可以利用Spring的管理能力,如bean的生命周期管理和事务管理,来更方便地创建和管理定时任务。 **Spring+Quartz动态定时任务创建** 将Spring与Quartz结合,我们可以方便地在运行时动态...
本项目旨在演示如何利用Spring和Quartz从数据库中动态加载定时任务,并实现对它们的增删改查功能。以下是关于这个主题的详细知识讲解。 首先,Quartz是一个开源的作业调度框架,它允许开发者创建、安排和执行定时...
总结来说,"spring+springMVC+mybatis+quartz动态定时任务创建"这个技术栈利用Spring的全面性、Spring MVC的Web处理能力、MyBatis的数据访问效率以及Quartz的定时任务管理,构建出一个能够灵活应对各种定时需求的...
在"Spring2.5+Quartz定时任务简单例子web工程"中,我们可以看到这两个框架的结合使用,主要目标是实现基于 Web 应用的定时任务管理。以下将详细介绍这个项目中可能涉及的知识点: 1. **Spring 2.5**: - **依赖...
通过整合SpringBoot和Quartz,我们可以利用Spring的自动配置能力,方便地在Spring应用中集成定时任务功能。 二、Quartz动态配置 1. 引入依赖:首先,在`pom.xml`文件中引入SpringBoot的SpringBoot-starter-quartz...
通过这个项目,开发者可以学习到如何在分布式环境中使用Quartz进行定时任务调度,以及如何利用Spring Batch进行批量数据处理。同时,了解如何将这些组件与MySQL数据库相结合,实现高可用性和可扩展性。对于希望构建...
在Spring Boot应用中整合Quartz定时任务是一种常见的需求,它可以帮助我们执行周期性的后台任务,如数据同步、报表生成等。Spring Boot与Quartz的结合提供了便捷的配置方式,特别是通过YAML(YAML Ain't Markup ...
在Spring中集成Quartz,可以充分利用Spring的上下文管理和生命周期管理特性,简化定时任务的配置和管理。 **Quartz** 是一个开源的作业调度框架,它支持复杂的调度策略,如cron表达式,能够创建、调度和执行作业。...
Spring通过`org.springframework.scheduling.quartz`包中的API,将这些概念与Spring容器集成,使得我们可以利用Spring的bean管理来创建和管理Job。 集成Quartz到Spring的过程中,主要涉及以下步骤: 1. 添加依赖:...
本项目“Springboot2-Quartz 后台可动态配置的定时任务”是基于SpringBoot 2.x版本与Quartz Scheduler整合的一个示例,它展示了如何在后台管理系统中动态地创建、更新和删除定时任务,以及监控这些任务的状态,为...
为了方便管理和监控定时任务,可以开发一个Web界面,利用Quartz提供的API查询Job和Trigger信息,支持新增、修改、删除等操作。此外,还可以查看任务执行日志,以便于排查问题。 总结,本Demo旨在通过SSM+Quartz的...
与Spring结合,我们可以利用Spring的依赖注入(DI)和声明式事务管理,使定时任务的管理和维护变得更加灵活和便捷。 要实现"spring+quartz整合,动态管理定时任务",首先需要在项目中引入Spring和Quartz的相关依赖...
在Spring中集成Quartz,我们可以利用Spring的优秀特性如依赖注入(Dependency Injection)和声明式事务管理,来更好地管理和控制定时任务。首先,我们需要在项目中引入`commons-collections.jar`这个库,这是一个...
Spring框架则提供了一种优雅的方式来整合Quartz,使我们能够利用Spring的依赖注入和管理功能来处理定时任务。 要实现Java Spring Quartz动态定时任务,首先需要在项目中引入Quartz库,通常通过Maven或Gradle添加...
在多应用服务器负载均衡环境下,Spring Quartz定时任务的重复执行问题是一个常见的挑战。Spring Quartz是一个强大的、开源的作业调度框架,允许开发者定义和执行复杂的定时任务。然而,当多个服务器实例并行运行时,...
下面我们将深入探讨如何利用Spring注解实现Quartz定时执行功能。 首先,我们需要引入Quartz和Spring的相关依赖。在Maven的pom.xml文件中添加以下依赖: ```xml <groupId>org.quartz-scheduler <artifactId>...
在【标题】"Spring的定时任务开发及对Quartz和Timer支持"中,涉及到的是Spring在处理定时任务方面的特性,这在企业级应用中非常常见,用于执行一些周期性的后台任务,如数据同步、报表生成、清理任务等。 首先,...
综上所述,Spring + Quartz 动态任务方案提供了一个灵活且高效的方式来管理定时任务。通过 Spring 容器的管理,使得任务的定义和调度变得更加便捷。同时,通过自定义的 `SchedulerService` 接口和其实现,可以实现对...
在Spring中集成Quartz,可以利用Spring的IoC容器管理作业和触发器,使得配置和管理变得更加简单。 要在Spring 2.0中使用Quartz执行每天定时任务,你需要按照以下步骤进行: 1. 引入依赖:在项目中添加Quartz的库...
本文将深入探讨如何在Spring中配置多个Quartz定时任务,并结合`quartz.properties`文件进行详细讲解。 首先,我们需要理解Quartz的基本概念。Quartz是开源的作业调度框架,允许应用程序在特定时间执行任务。它支持...