Spring与Quartz集成看起来简单,其实还有很多小的细节解决起来比较耗时,本文作为备忘,摘要代码以供参考。
环境:Spring 3.2,Quartz 2.2.x
数据库:mysql 5.5+
一、普通定时器,非Cluster环境:即没有使用Quartz Cluster,每个Spring实例中的定时器都会同时运行,这在分布式环境中并不会带来太多额外的问题,如果你的定时器中有操作数据库的情况,请注意多个定时器同时运行带来的并发问题。
1、spring-quartz.xml
<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="jobFactory"> <bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory"/> </property> <property name="autoStartup" value="true"/> <property name="triggers"> <list> <ref bean="testTrigger" /> </list> </property> </bean> <bean id="testTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="cronExpression" value="0/10 * * * * ?" /> <property name="jobDetail" ref="testJobDetail" /> </bean> <bean id="testJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="testJob"/> <property name="targetMethod" value="execute"/> <property name="concurrent" value="false"/> </bean> <bean id="testJob" class="com.test.support.service.job.TestJob"/>
2、TestJob.java
public class TestJob{ private StudentManager studentManager; public void setStudentManager(StudentManager studentManager) { this.studentManager = studentManager; } public void execute() { System.out.println("test" + studentManager); } }
通过上述方式,我们可以看到TestJob无需实现任何额外的接口,只需要借助“MethodInvokingJobDetailFactoryBean”即可实现Job中依赖注入其他Spring Bean,因为此时testJob已经被声明为Spring bean。除此之外,其他的方式均需要额外的操作,才能将spring bean注入到Job中。
二、Cluster方式:基于JDBC方式,将Quartz job、trigger都序列化到数据库中,这种方式的好处也非常明显,当部署多个spring实例时,集群中任何时候“Scheduler”获取lock,并负责管理所有的job和trigger。Cluster模式下,有很多小细节难以解决,而且与spring的集成并不是非常良好,比如jobData必须实现Serializable接口、Job实例无法透明的注入spring bean等。
首先到Quartz的官网,下载quartz的源代码,并找到相应的数据库sql脚本,并执行,最好为quartz相关的表单独创建一个database。执行SQL脚本的主要作用是生成Cluster所需要的table,这一步quartz并不会在运行时为我们完成。(可能脚本有书写错误,比如mysql的script需要修改一处拼写错误)
1、spring-quartz.xml
<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="dataSource" ref="quartzDataSource" /> <property name="quartzProperties"> <props> <prop key="org.quartz.scheduler.instanceName">quartz-cluster</prop> <prop key="org.quartz.scheduler.instanceId">AUTO</prop> <!-- 线程池配置 --> <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop> <prop key="org.quartz.threadPool.threadCount">6</prop> <prop key="org.quartz.threadPool.threadPriority">5</prop> <!-- JobStore 配置 --> <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop> <!-- 集群配置 --> <prop key="org.quartz.jobStore.isClustered">true</prop> <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop> <prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">1</prop> <prop key="org.quartz.jobStore.misfireThreshold">120000</prop> <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop> </props> </property> <property name="schedulerName" value="clusterScheduler" /> <property name="startupDelay" value="15" /> <property name="applicationContextSchedulerContextKey" value="applicationContext" /> </property> <property name="overwriteExistingJobs" value="true" /> <property name="autoStartup" value="true" /> <property name="triggers"> <list> <ref bean="testTrigger" /> </list> </property> </bean> <bean id="testTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="cronExpression" value="0/4 * * * * ?" /> <property name="jobDetail" ref="testJobDetail" /> </bean> <bean id="testJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="com.test.support.service.job.TestJob" /> <property name="durability" value="true" /> </bean> <bean id="quartzDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.1.121:3306/quartz?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> <property name="maxActive" value="6"></property> <property name="maxIdle" value="1"></property> <property name="minIdle" value="1"></property> <property name="maxWait" value="60000"></property> <property name="defaultAutoCommit" value="true"></property> </bean>
2、TestJob.java
public class TestJob extends QuartzJobBean{ private StudentManager studentManager; public void executeInternal(JobExecutionContext context) throws JobExecutionException { //因为spring无法注入bean,所以需要手动从ApplicationContext中获取 setting(context); System.out.println("test" + studentManager); } protected void setting(JobExecutionContext context) { try { //key来自spring-quartz.xml中“applicationContextSchedulerContextKey”的值 ApplicationContext applicationContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext"); studentManager = (StudentManager) applicationContext.getBean("studentManager"); } catch (Exception e) { throw new RuntimeException(e); } } }
目前spring尚没有内置的机制来解决自动注入的问题,所以job中的spring bean需要引入一些外部的机制。上述办法就是其中一个。此外还有一种方式:继承SpringBeanJobFactory。
1)spring-quartz.xml
.... <property name="applicationContextSchedulerContextKey" value="applicationContext" /> <property name="jobFactory"> <bean class="com.test.support.service.AutowireSpringBeanJobFactory" /> </property> ....
<context:annotation-config /> <context:component-scan base-package="com.test.support.service.job" />
2)AutowireSpringBeanJobFactory.java
public class AutowireSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } }
3)TestJob.java
@Service public class TestJob extends QuartzJobBean{ @Autowired private StudentManager studentManager; public void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("test" + studentManager); } }
通过继承SpringBeanJobFactory可以实现springBean的自动注入,前提是必须使用“Autowired”注释,不过开发者一定有办法让注释和setter方式同时支持。本人不太喜欢这种方式,感觉和spring耦合过度了,或许不久的将来,spring会提供内部机制解决这些问题。
还有一个很严重的问题,如果基于cluster,那么trigger的class信息将会被序列化到DB中,此后即使从quartz Scheduler配置文件中移除了某个trigger,那么这个trigger仍然会触发,除非删除trigger相应的job类(或者从db中删除trigger的记录),这个问题实在是难以想象,解决这个问题需要重写spring + Quartz的很多集成类,实在是有些麻烦,希望quartz cluster机制能够更加完善。
上述我们提到的几个问题,或许在将来的spring-quartz项目中能够得以解决。否则开发者需要补充太多东西,才能将此架构完善。
相关推荐
Java_Spring与Quartz的整合
关于spring中quartz的配置
spring+quartz demo,下载后即可运行,很强大哦....
本项目来源与网络,本人对项目...直接通过mvn 倒入项目,在Spring-quartz-demo\src\main\webapp\sql 有sql 建立数据库,表 启动tomcat 直接访问http://localhost:8080/Spring-quartz-demo/task/taskList.htm 就可以使用
spring的quartz使用实例,spring的quartz使用实例
spring整合quartz文档
spring注解Quartz定时执行功能
spring 集成quartz定时任务 用数据库实现quartz的集群
所需jar如下: spring-beans-3.2.4.RELEASE.jar spring-core-3.2.4.RELEASE.jar spring-expression-3.2.4.RELEASE....quartz-all-2.1.7.jar spring-tx-3.2.4.RELEASE.jar slf4j-log4j12-1.6.1.jar slf4j-api-1.6.1.jar
spring集成quartz使用需要的jar包下载。
Spring_QuartZDemo
Spring+quartz相关jar包.rar
好像很多人整合出现了问题,所以抽个demo出来 是一个demo包含源码和lib 在tomcat下可以执行
这个是spring 和 quartz的集成,quartz是单独的包,java线程的方式运行,利用自定义Jobfactory来解决spring注入service空指针的问题,简单实例执行main方法即可,很实用
spring-quartz简单实例 简单的quartz定时任务实例代码
spring+quartz定时小例子,架包+文档 我自己整理的
Quartz学习文档 Spring + Quartz配置详细实例 jar包 Quartz时间格式设置
spring-quartz的标准配置文件
针对于spring集成quartz配置进行详细说明,并提供部分简单示例说明配置。
spring整合quartz定时任务调度..........