Spring目前支持两种定时任务集成,Timer(since JDK1.3)和Quartz,这篇主要说说Quartz。
将Quartz集成到Spring主要是通过创建FactoryBean并为其添加一些引用。
而且我们可以通过MethodInvokingFactoryBeans非常方便的引用任何一个对象的方法。
首先需要理清Quartz本身的一些概念,Trigger、Job、JobDetail等等。
(请参考http://quartz-scheduler.org/documentation/quartz-2.2.x/tutorials)
为了简化其在Spring应用中的使用,Spring为用户提供了一些相关的class。
1.org.springframework.scheduling.quartz.JobDetailBean:
JobDetail对象拥有某个任务的所有信息。
相应地,Spring提供了JobDetailBean。
需要注意的是,JobDetailBean不适用于Quartz2.0以上的版本。
如果需要考虑兼容性,我们可以用JobDetailFactoryBean来代替。
顺便一提,我加的dependency是2.2.1的,我只能用JobDetailFactoryBean:
<!-- quartz scheduler --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.1</version> </dependency>
下面是一个例子,定义一个job:
public class MinutePrintJob extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("Current Time:::"+new Date()); } static final Logger logger = LoggerFactory.getLogger(MinutePrintJob.class); }
定义一个job bean:
<bean id="printJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="pac.king.schedule.MinutePrintJob" /> <property name="durability" value="true" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="5" /> </map> </property> </bean>
上面的例子中我们将timeout属性放到了jobDataAsMap中。(PS:JobDataMap对象可以在JobExecutionContext中获取)
另外JobDetailBean也可以将JobDataMap中的属性映射到某个Job对象的field中,即我们也可以在Job中得到这些属性。
(还是PS:别忘了setter)
比如以下代码,timeout输出5:
public class MinutePrintJob extends QuartzJobBean { private int timeout; public void setTimeout(int timeout) { this.timeout = timeout; } @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("Current Time:::"+new Date()+"\t timeout::"+timeout); } static final Logger logger = LoggerFactory.getLogger(MinutePrintJob.class); }
2.org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean:
如果我想调用某个已定义的方法,但我又觉得再新建一个class写一行代码去调用这个方法很蠢。
此时我们可以用MethodInvokingJobDetailFactoryBean,仅仅改变一下配置就让它生效。
比如,我在MinutePrintJob中加了个...(当然他不是复写QuartzJobBean的方法..你可以把他定义在任何地方)
public void justDoIt(){ logger.info("Kim say:just do it!!"); }
定义一个methodInvokingJobDetailFactoryBean....并根据需要让特定的trigger负责该job:
<bean id="minPrintJob" class="pac.king.schedule.MinutePrintJob"></bean> <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="minPrintJob" /> <property name="targetMethod" value="justDoIt" /> </bean>
需要注意的是,默认时job都是无状态的,多个job可以影响彼此。
比如两个trigger引用了相同的JobDetail,并且在第一个job没有结束执行,第二个job就开始执行。
我们可以为JobDetail加个状态以让job不要并发执行。
MethodInvokingJobDetailFactoryBean的concurrent属性恰恰是解决这一问题的:
<bean id="minPrintJob" class="pac.king.schedule.MinutePrintJob"></bean> <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="minPrintJob" /> <property name="targetMethod" value="justDoIt" /> <property name="concurrent" value="false" /> </bean>
(PS:concurrent默认为true!)
3.CronTriggerBean and SimpleTriggerBean:
声明好job和jobDetail后,我们需要为他们安排时间执行。
虽然Quartz提供了一些trigger,Spring还额外提供了两个子类。
这两个类没有太多说明(两者javadoc的内容几乎相同),仅用于简化配置,使用方法如下:
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="jobDetail"/> <property name="startDelay" value="5000"/> <property name="repeatInterval" value="15000"/> </bean> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="exampleJob"/> <property name="cronExpression" value="0 0 6 * * ?"/> </bean>
(cronExpression格式请参考http://quartz-scheduler.org/api/2.1.0/org/quartz/CronExpression.html)
剩下的工作就是将这些trigger配置到SchedulerFactoryBean:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger"/> <ref bean="simpleTrigger"/> </list> </property> </bean>
更多属性根据具体需求进行设置
另外,我用的是MySQL。
虽然可以在schedulerFactoryBean中配置dataSource,但我还是在quartz.properties中配置了数据源。
但这样就不能指定自己喜欢的Database Connection Pools实现,quartz默认用c3p0作为实现。
仅供参考:
#============================================================================ # Configure Datasources #============================================================================ org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 org.quartz.dataSource.myDS.user = root org.quartz.dataSource.myDS.password = org.quartz.dataSource.myDS.maxConnections = 5
【Quartz】Integration with Spring