这个博客介绍quartz集成spring,之前用的是spring3,既然现在spring4已经出了很久了,再学习的话当然用的是4,我这里使用的是最新的spring4.2.4.RELEASE,创建的maven项目的pom如下,其中spring中关于quartz的包都在spring-context-support这个包中。
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<exclusions>
<exclusion> <!-- 不懂为啥依赖C3P0 -->
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
</exclusion>
</exclusions>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
spring和quartz结合一共就五个主要的类
1、JobDetailFactoryBean
这个是创建jobDetail的。这个类很简单,就是使用了代理模式,将quartz正真的jobDetail设置作为一个属性,并通过其他的属性配置生成一个quartz的jobDetail,实现类是JobDetailImpl,
有一个关键的属性是:jobClass,这个表示job的类,可以通过反射来生成一个实例,生成的job实例的JobDataMap可以获得applicationContext的引用,引用是通过applicationContextJobDataKey来从JobDataMap中获取的。
但是对这个job类是有点限制的,必须继承自QuartzJobBean抽象类(这个类是实现了quartz的Job接口),然后执行的任务都会在executeInternal(JobExecutionContext context)方法中定义。
这个类允许在这个bean中获得applicationContext,通过这个属性applicationContext。其他的配置都很简单
所有的关键代码都在这个类的afterPropertiesSet方法中,顺便复习一下spring的知识,如果一个bean实现了InitailizingBean,那么这个bean在创建完成后会执行他的afterPropertiesSet方法,在spring容器关闭时调用destroy方法。
@Override @SuppressWarnings("unchecked") public void afterPropertiesSet() { if (this.name == null) { this.name = this.beanName;//如果没有指定name,则将这个bean的id或者name属性赋给他, } if (this.group == null) { this.group = Scheduler.DEFAULT_GROUP; } if (this.applicationContextJobDataKey != null) { if (this.applicationContext == null) { throw new IllegalStateException( "JobDetailBean needs to be set up in an ApplicationContext " + "to be able to handle an 'applicationContextJobDataKey'"); } getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext); } JobDetailImpl jdi = new JobDetailImpl(); jdi.setName(this.name); jdi.setGroup(this.group); jdi.setJobClass((Class) this.jobClass); jdi.setJobDataMap(this.jobDataMap); jdi.setDurability(this.durability); jdi.setRequestsRecovery(this.requestsRecovery); jdi.setDescription(this.description); this.jobDetail = jdi; }
我的测试中在spring的xml中的配置,和源码如下:
<!-- 创建jobDetail, --> <bean id="hellojob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="task.HelloJob"></property> <property name="name" value="hellojob"/> <property name="group" value="default" /> <property name="applicationContextJobDataKey" value="applicationContext" /> </bean>
我的Java代码
public class HelloJob extends QuartzJobBean{ ApplicationContext applicationContext = null; public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { Object o = getApplicationContext().getBean("hellojob"); System.out.println(o == null); } }
2、MethodInvokingJobDetailFactoryBean
这个类也是产生jobDetail的bean,但是不同于上面那个需要继承自某个类或者是实现某个接口,这个类可以指定作为job的类和要调用的方法,然后通过反射的形式定时地执行这个方法,这也和名字MethodInvoking想匹配。
我在看这个类的源码时颇费了一番功夫,因为源码很长,而且有一个关键的类我没有吓到源码,不知道为何maven不给我下这个类的源码,不过幸运的是最终我将这个类的源码拿下,现在记录一下。
javadoc中关键记录:
1、这个类集成了methodInveoker的属性,比如setTargetObject,setTargetMethod,也可以设置target beanname(spring的applicationContext中的beanname)来替代targetObject,这样就避免了每一次都去创建一个新的实例。方法可以是静态方法或者是非静态方法。
2、支持并发的执行和非并发的执行,通过设置concurrent的boolean属性值,默认情况下是支持并发的(关于并发性我们在之前的quartz中已经提到了,我说的情况是每一次执行完成的时间大于间隔执行的时间)。通过这个类创建的jobDetail都是持久的,也就是即使这个工作执行完成后以后再也不会再执行了也不会被丢弃,而是存在jobStore中。关于这个的设置在源码中得到体现,下面我会贴出源码的。
类的属性和方法,很简单的属性我就放过了。
1、targetBeanName 当要执行的方法所在的类的实例在spring的applicationContext中时 ,指定这个bean的name(或者id),这个可以替代targetObject,这个不用每一次在执行时都创建一个实例,但是如果同时指定了targetObject或者是targetClass,则这个配置不会起作用。源码中的英文如下
/**
* Set the name of the target bean in the Spring BeanFactory.
* <p>This is an alternative to specifying {@link #setTargetObject "targetObject"},
* allowing for non-singleton beans to be invoked. Note that specified
* "targetObject" and {@link #setTargetClass "targetClass"} values will
* override the corresponding effect of this "targetBeanName" setting
* (i.e. statically pre-define the bean type or even the bean object).
*/
2、afterPropertiesSet 这个方法会在bean初始化完成后执行
里面显示调用了prepare方法,这个方法在其父类里面,是找到对应的方法和对象,不用管。
里面建立了JobDeatilImpl,需要注意的有两个,一个是jobClass,先把源码贴出来
// Consider the concurrent flag to choose between stateful and stateless job. Class<?> jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class); // Build JobDetail instance. JobDetailImpl jdi = new JobDetailImpl(); jdi.setName(name); jdi.setGroup(this.group); jdi.setJobClass((Class) jobClass); jdi.setDurability(true); jdi.getJobDataMap().put("methodInvoker", this); this.jobDetail = jdi;
当concurrent是true时,jobClass是methodInvokingJob,不是时是StateFulMethodInvoklingJob,其实这两个类是一样的,之不贵后者在类上面加了两个注解,就是我们之前提及的注解:@PersistJobDataAfterExecution @DisallowConcurrentExecution,表示不能并发执行,并且对Map的操作会影响JobDetail的map。这量个jobClass作为被调用时的job调用的方法都是MethodInvokingJo的executeInternal方法,因为StateFulMethodInvokingJob类是继承的MethodInvokingJob,这个方法的源码如下:
@Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { context.setResult(this.methodInvoker.invoke()); } catch (InvocationTargetException ex) { if (ex.getTargetException() instanceof JobExecutionException) { // -> JobExecutionException, to be logged at info level by Quartz throw (JobExecutionException) ex.getTargetException(); } else { // -> "unhandled exception", to be logged at error level by Quartz throw new JobMethodInvocationFailedException(this.methodInvoker, ex.getTargetException()); } } catch (Exception ex) { // -> "unhandled exception", to be logged at error level by Quartz throw new JobMethodInvocationFailedException(this.methodInvoker, ex); } }
在这个源码中,他是调用的methodInvoker.invoke方法,我们先不管这个方法是如何调用的,先来看一下什么是methodInvoker,在MethodInvokingJob中,根本就没有实例化MethodInvoker,怎么能调用呢?当时我也被难住了,直到我看到上面的在创建JobDetailImpl时,有这么一行代码
jdi.getJobDataMap().put("methodInvoker", this)
我突然想起来,原来他是通过调用时穿进去的methodInvoker实例(因为每一次调用都会建立一个job对象,并且执行某些setter方法,这个在之前已经说过),this就是代表的MethodInvokingJobDetailFactoryBean,他是MethodInvoker的子类,随意最终调用的就是这个MethodInvokingJobDetailFactoryBean实例的invoke方法,他并没有复写,所以使用的还是MethodInvoker中的invoke方法,这个方法执行的之前prepare方法中设定的targetObject targetMethod,使用的方式是反射。
至此我们有已经完全看懂了这个类的源码,写的非常的巧妙,但是同时很耗费时间。
我测试的spring的xml的配置和java代码
<bean id="hellojob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="methodInvokingJob"/> <property name="targetMethod" value="kaishigan"/> </bean> <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="name" value="simpleTrigger"/> <property name="group" value="default"></property> <property name="repeatCount" value="3"></property> <property name="repeatInterval" value="2000"></property> <property name="startDelay" value="4000"></property> <property name="jobDetail" ref="hellojob"/> </bean> <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="simpleTrigger"/> </list> </property> <property name="startupDelay" value="10"/><!-- 等待10秒之后才执行,让容器启动成功。单位是秒 --> </bean>
public class MethodInvokingJob { public void kaishigan(){ System.out.println("xxxx"); } }
上面的代码是我已经测试好的,但是在写完这么多之后我还是有疑问,在上面的xlm中,simpleTrigger这个bean的属性jobDetail我们指向的是hellojob这个bean,也就是methodInvokeingJobDetailFactoryBean 这个实例,但是methodInvokeingJobDetailFactoryBean类和jobDetail没有任何关系啊,谁都不是谁的子类,他怎么能够引用呢?如果有大神看到这里,请给我留言或者是加我qq,1902442871或者1308567317.
3、SimpleTriggerFactoryBean
这个类似于我们在quartz中创建SimpleTrigger的类,他创建的就是一个SimpleTriggerImpl类,在这个类的afterpropertiesSet方法中存在。
这个类的javadoc中是这么说的:SimpleTrigger已经是一个javabean的对象了,但是他缺乏合理的默认值,这个类(SimpleTriggerFactoryBean)提供了很多的缺省值:beanname是这个trigger的缺省的name值,quartz默认的group是缺省的group值,当前时间是开始时间,循环次数为无线次。
在这个类的afterPropertiesSet方法中,可以发现如果在trigger中设置了jobDetail的话,会将其放置到trigger的jobDataMap中,key为jobDetail
代码如下:
@Override public void afterPropertiesSet() { if (this.name == null) { this.name = this.beanName; } if (this.group == null) { this.group = Scheduler.DEFAULT_GROUP; } if (this.jobDetail != null) { this.jobDataMap.put("jobDetail", this.jobDetail); } if (this.startDelay > 0 || this.startTime == null) { this.startTime = new Date(System.currentTimeMillis() + this.startDelay); } SimpleTriggerImpl sti = new SimpleTriggerImpl(); sti.setName(this.name); sti.setGroup(this.group); sti.setJobKey(this.jobDetail.getKey()); sti.setJobDataMap(this.jobDataMap); sti.setStartTime(this.startTime); sti.setRepeatInterval(this.repeatInterval); sti.setRepeatCount(this.repeatCount); sti.setPriority(this.priority); sti.setMisfireInstruction(this.misfireInstruction); sti.setDescription(this.description); this.simpleTrigger = sti; }
这个类也很简单,类似于JobDetailFactoryBean,他比quartz原声的simpleTrigger多了些功能,比如可以获得spring的applicationcontext。
4、CronTriggerFactoryBean
这个类提供我们之前的CronTrigger的功能,他的配置和上面的simpleTrigger一样,这里直接略过,把我的xml配置和代码贴出来:
public class MethodInvokingJob { public void kaishigan(){ System.out.println("xxxx"); } }
<bean id="methodInvokingJob" class="task.MethodInvokingJob"/> <bean id="hellojob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="methodInvokingJob"/> <property name="targetMethod" value="kaishigan"/> </bean> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="cronExpression" value="*/2 * * * * ? *"/> <property name="jobDetail" ref="hellojob"/> </bean> <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger"/> </list> </property> <property name="startupDelay" value="10"/><!-- 等待10秒之后才执行,让容器启动成功。单位是秒 --> </bean>
5、SchedulerFactoryBean
这个是spring给我们提供的类,提供的是quartz中的SchedulerFactory的功能。下面对这个类的源码做些记录:
1、这个类中含有SchedulerFactory属性,就是quartz中真正的SchedulerFactory,但是允许我们配置实现类,配置这个类的schedulerFactoryClass属性指定实现类的类型,默认是StdSchedulerFactory.class,配置configLocation属性指定配置文件的位置,如果不是StdSchedulerFactory的话,必须写这个,配置是直接配置quartzProperties这个属性。1
2、schedulerContextMap 这个属性用来保存一些属性,可以在job执行时获取。
3、applicationContextSchedulerContextKey 可以配置spring的applicationcontext在这个scheduler中的schedulerContext中可以获得,获得时的键就是这里指定的key,关于这点的内容可以在这个方法:populateSchedulerContext中仔细查看。
4、autoStartup 调度器是不是立刻启动,默认是true.
5、phase 我没明白这个属性的意思,javadoc如下:/**
* Specify the phase in which this scheduler should be started and
* stopped. The startup order proceeds from lowest to highest, and
* the shutdown order is the reverse of that. By default this value
* is Integer.MAX_VALUE meaning that this scheduler starts as late
* as possible and stops as soon as possible.
*/
6、startupDelay 这个属性很重要,默认是0.表示只要这个SchedulerFactoryBean一创建,就要执行(当然前提是autostart是true),最好设置一些值,比如10秒以等待整个application启动完成,除非我们有特殊情况。单位是秒,不是毫秒,在这个类的这个方法中有提到:startScheduler
7、waitForJobsToCompleteOnShutdown 在停止spring的applicationcontext时要不要等待任务执行完成,默认是false。
8、applicationContext 用来指向spring的applicationContext。
9、这个方法比较值得一看:start方法,如果指定的startDelay合理的话,操作的明细如下:
protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException { if (startupDelay <= 0) { logger.info("Starting Quartz Scheduler now"); scheduler.start(); } else { if (logger.isInfoEnabled()) { logger.info("Will start Quartz Scheduler [" + scheduler.getSchedulerName() + "] in " + startupDelay + " seconds"); } Thread schedulerThread = new Thread() { //新开启一个线程,异步处理 @Override public void run() { try { Thread.sleep(startupDelay * 1000);//可以看出,startupDelay的单位是秒。 } catch (InterruptedException ex) { // simply proceed } if (logger.isInfoEnabled()) { logger.info("Starting Quartz Scheduler now, after delay of " + startupDelay + " seconds"); } try { scheduler.start(); } catch (SchedulerException ex) { throw new SchedulingException("Could not start Quartz Scheduler after delay", ex); } } }; schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "]"); schedulerThread.setDaemon(true); schedulerThread.start(); } }
相关推荐
Spring与Quartz的集成是另一个重要的知识点。Spring框架提供了一种优雅的方式来管理和配置Quartz,使得我们可以利用Spring的依赖注入特性来创建和管理Job实例。通过Spring的`SchedulerFactoryBean`,可以轻松地在...
标题中的"spring-context-support-4.2.2和quartz-2.2.3所需Jar包"涉及到两个关键的Java库:Spring Context Support 4.2.2版本和Quartz Scheduler 2.2.3版本。这两个库在企业级Java应用开发中扮演着重要角色,特别是...
在 "quartz-spring-demo" 这个项目中,我们将会看到如何在 Spring 应用程序中集成 Quartz。以下是一些关键的知识点: 1. **Quartz 简介**:Quartz 提供了一个完全线程化的模型来执行定时任务,支持复杂的调度策略,...
3. "spring-context-support-4.2.6.RELEASE.rar"和"spring-context-support-4.2.2.rar":这可能是两个不同版本的Spring上下文支持模块,包含了对邮件服务、JMS消息、Quartz调度器等集成的支撑。 结合以上信息,我们...
学习Quartz和Spring-Quartz,不仅需要理解它们的基本概念,还要掌握如何在实际项目中进行配置和使用。例如,创建一个定时任务,你需要定义Job类,配置Trigger,然后在Spring的配置文件中设置Scheduler。此外,熟悉...
在Spring框架中集成Quartz,可以方便地创建和管理定时任务,使得开发者无需关注底层调度逻辑,专注于业务逻辑的实现。"quartz-2.1.6.zip"这个压缩包包含了使用Quartz在Spring中构建定时任务所需的各种组件和文档。 ...
而当我们提到`spring-context-support.jar`和`quartz-all-1.6.0.jar`时,我们是在讨论Spring框架中的任务调度功能,特别是与Quartz库的集成。 `spring-context`是Spring框架的核心模块之一,它提供了上下文...
**Spring与Quartz集成** 将Spring与Quartz结合,可以利用Spring的管理能力和Quartz的调度能力。首先,你需要在Spring配置文件中声明一个`SchedulerFactoryBean`,这将初始化并管理Quartz调度器。然后,通过定义`...
总结起来,"spring-quartz-021"是一个学习和实践Spring与Quartz集成的实例,它展示了如何利用Spring的管理能力,配合Quartz的强大调度功能,实现基于corn表达式的定时任务。对于Java开发者来说,掌握这种集成方式能...
quartz-spring-manager, spring的xml的一个管理器 jar 集成 用户界面是 Bootstrap 欢迎使用 Quartz spring 管理器 !spring的xml的jar 集成,可以用于 web 。如何使用:将/src/main/webapp/scheduler 复制到你将 ...
总结来说,"spring-quartz-example"是一个很好的学习资源,它演示了如何在Spring环境中集成和配置Quartz,创建并管理定时任务。通过分析这个项目,开发者不仅可以掌握Spring与Quartz的结合使用,还能了解到如何设计...
在 "spring-quartz-02" 这个项目中,我们可以看到如何将 Spring 框架与 Quartz 整合,以实现定时任务的功能。这个 demo 的核心目标是展示如何配置和使用 Spring 对 Quartz 的支持,通过 Cron 表达式来定义任务的执行...
implementation 'org.springframework.boot:spring-boot-starter-quartz' ``` 接下来,创建一个Quartz作业类(Job)并实现`org.quartz.Job`接口。这个类将定义实际要执行的任务。例如: ```java import org.quartz...
综上所述,这个压缩包提供的 Quartz 2.2.2 版本和 Spring Boot 2.x 的集成示例,对于学习和使用 Quartz 进行任务调度具有很高的参考价值。无论是初学者还是经验丰富的开发者,都可以从中受益,快速掌握如何在 Spring...
标题中的“Spring 2.0 Quartz 执行每天定时任务”是指使用Spring框架的Quartz集成来创建一个每天自动执行的任务。Quartz是一个开源的作业调度框架,它允许开发者定义和调度任务,以便在指定的时间自动运行。在Spring...
本示例 "Multiple-Jobs-in-Quartz-Spring-Example" 旨在展示如何在 Spring 应用中集成多个 Quartz 任务。对于Java开发人员来说,这可能是了解如何在实际项目中使用这两种技术协同工作的一个绝佳实践。 首先,我们...
在 "spring-quartz-01" 这个项目中,开发者已经集成 Spring 和 Quartz,创建了一个可以定时执行任务的示例。这通常涉及到以下几个关键知识点: 1. **Spring 集成 Quartz**:首先,我们需要在 Spring 的配置文件中...
- **Spring 集成**:在 Spring 应用中,可以通过 XML 配置或 Java Config 方式来配置和初始化 Scheduler。 3. **数据库表结构** - Quartz 使用数据库存储 Jobs 和 Triggers,因此需要创建相应的数据库表。在提供...
【Spring与Quartz集成详解】 在Java开发中,我们经常需要进行定时任务的调度,以实现后台自动执行某些业务逻辑。Spring框架提供了丰富的功能,而Quartz是一个强大的、完全开源的作业调度框架,用于在Java应用程序中...
同时,Quartz还支持Spring框架的集成,可以利用Spring的依赖注入特性来管理任务实例。 在1.8.6这个版本中,可能修复了一些已知的bug,提升了性能,或者增加了新的特性和功能。为了深入了解Quartz 1.8.6,你需要解压...