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

		
		
			org.quartz-scheduler
			quartz
			2.2.1
		
		
			org.quartz-scheduler
			quartz-jobs
			2.2.1
		


下面是一个例子,定义一个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:

	
		
		
		
			
				
			
		
	


上面的例子中我们将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:

	
	
		
		
	


需要注意的是,默认时job都是无状态的,多个job可以影响彼此。

比如两个trigger引用了相同的JobDetail,并且在第一个job没有结束执行,第二个job就开始执行。

我们可以为JobDetail加个状态以让job不要并发执行。

MethodInvokingJobDetailFactoryBean的concurrent属性恰恰是解决这一问题的:

	
	
		
		
		
	

(PS:concurrent默认为true!)



3.CronTriggerBean and SimpleTriggerBean

声明好job和jobDetail后,我们需要为他们安排时间执行。

虽然Quartz提供了一些trigger,Spring还额外提供了两个子类。


这两个类没有太多说明(两者javadoc的内容几乎相同),仅用于简化配置,使用方法如下:


  
  
  

 

  
  

(cronExpression格式请参考http://quartz-scheduler.org/api/2.1.0/org/quartz/CronExpression.html)



剩下的工作就是将这些trigger配置到SchedulerFactoryBean:


  
    
      
      
    
  


更多属性根据具体需求进行设置 i_f29.gif

另外,我用的是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