在Spring中使用Quartz

18 October 2010

Spring 提供了几个帮助类用于在应用中做调度,包括JDK Timer类和OpenSymphony Quartz Scheduler两种.

Quartz基础

Quartz包括五种主要结构用于实现调度:

  • Job接口
  • JobDetail类
  • Trigger 抽象类
  • Scheduler接口
  • SchedulerFactory 接口

Job接口表示一个作业(job)。一个作业专注做一件事。它的API非常简洁。只有一个execute方法,该方法在作业被执行时有Quartz调度。该方法有一个JobExecuteContext参数,可以通过该参数给execute()方法传递有用信息。

public interface Job{
    void execute(JobExecuteContext ctx);
}

一些数据可以通过JobDataMap传递给作业。如果一个JobDataMap被注册到JobDetail中,就能够在作业中通过 JobExecuteContext来访问。JobDetail用来描述一个特定Job的信息。Job通过触发器(Trigger)触发。Quartz提 供了集中Trigger的实现,如SimpleTrigger和CronTrigger。SimpleTrigger类似一个简单时钟,你可以定义开始是 建,结束时间,重复次数,重复周期。CronTrigger类似Linux系统中的cron。CronTrigger的设置可以非常详细,如在每个月最后 一个周五的上午10:15执行作业。需要注意的是Trigger和Job是具名的,可以被赋值给一个组,在同一组内不能出现同名。你可以对一个组创建一个 触发器,在该组内的所有Job都将会执行。
SchedulerFactory 用于获得Scheduler实例,可以用于注册作业和触发器。
实现一个简单的实例:每十秒钟打印一次欢迎。
首先实现一个作业:

public class SimpleJob implements Job {
	@Override
	public void execute(JobExecutionContext arg0)
	throws JobExecutionException {
		System.out.println("[JOB] Welcome to Quartz!");
	}
}

定义一个Scheduler,注册触发器和作业:

public class SimpleSchedule {
	public static void main(String[] args) {
		SchedulerFactory factory=new StdSchedulerFactory();
		try {
			Scheduler scheduler = factory.getScheduler();
			scheduler.start();
										            
			JobDetail jobDetail = new JobDetail("SimpleJob",null, SimpleJob.class);
			Trigger simplerTrigger = TriggerUtils.makeSecondlyTrigger(10);
			simplerTrigger.setName("SimpleTrigger");
										            
			scheduler.scheduleJob(jobDetail, simplerTrigger);
		}catch (SchedulerException e) {
		    // TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

执行过后,每十秒输出[JOB] Welcome to Quartz!

Spring中的Quartz

Spring中的Quartz API位于org.springframework.scheduling.quartz包中。主要类结构包括:

  • QuartzJobBean 抽象类
  • JobDetailBean
  • SimpleTriggerBean
  • CronTriggerBean
  • SchedulerFactoryBean
  • MethodInvokingDetailFactoryBean

很明显对应实现Quartz中相应的接口。QuartzJob实现Job,JobDetailBean继承JobDetail。 SimpleTriggerBean和CronTriggerBean继承自相应的Trigger。 MethodInvokingJobDetailFactoryBean用于在类中调用任何对象的方法。
声明Job

JobDetailBean用于声明作业。可以为其设置作业名,以及需要的数据。

<bean name="simpleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
	<property name="jobClass" value="com.alibaba.jiang.learn.quartz.SimpleJob" />
	<property name="jobDataAsMap">
		<map>
			<entry key="message" value="Welcome to Quartz" />
		</map>
	</property>
</bean>

实现Job类:

public class SimpleJob extends QuartzJobBean {
	@Override
	protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
		String message = ctx.getJobDetail().getJobDataMap().getString("message");
		System.out.println(message);
	}
}

还可以通过setter注入的方式注入message。
声明触发器:

<bean name="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
	<property name="jobDetail" ref="simpleJob"/>
	<property name="startDelay" value="0"/>
	<property name="repeatInterval" value="10000"/>
</bean>

声明调度器,设置Job和Trigger:

<bean name="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<property name="triggers">
		<list>
			<ref bean="simpleTrigger"/>
		</list>
	</property>
</bean>

所有都设置好后,可以通过加载Context,调度器将自动执行:

public class SimpleSpringQuartz {
	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
	}
}

使用MethodInvokingJobFactoryBean

上面的范例使用的是Quartz Job,事实上在Spring中可以使用自定义Pojo Bean,无须继承自QuartzJobBean。
首先声明一个PojoBean

<bean name="welcomeBean" class="com.alibaba.jiang.learn.quartz.WelcomeBean">
	<property name="message" value="Welcome to Quartz Method"/>
</bean>

对应的Pojo Bean:

public class WelcomeBean {
    private String message;
	public void setMessage(String message) {
		this.message = message;
	}
	public void welcome(){
		System.out.println(message);
	}
}

声明MethodInvokingJobDetailFactoryBean:

<bean name="methodInvokingJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	<property name="targetObject" ref="welcomeBean"/>
	<property name="targetMethod" value="welcome"/>
</bean>
<bean name="methodTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
	<property name="jobDetail" ref="methodInvokingJob" />
	<property name="startDelay" value="0"/>
	<property name="repeatInterval" value="10000"/>
</bean>

注意

一个触发器只能触发一个Job,不过一个Job可以有多个Trigger触发,这回带来并发问题。在Quartz中,如果你不想并发执行一个同一个 Job,你可以实现StatefulJob,而不是Job。在Spring中如果使用 MethodInvokingJobDetailFactoryBean,可以通过设置concurrent="false"属性来实现。

尾注

在Spring中使用Quartz而不是单独的一个应用的好处包括:

  • 将所有的任务调度设置放在同一个地方,是任务易于维护。
  • 只对Job编码,Trigger和Scheduler可以通过配置设置
  • 可以使用Pojo Java Bean执行job,而无需实现Job接口


blog comments powered by Disqus