`
sundoctor
  • 浏览: 324333 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Quartz 在 Spring 中如何动态配置时间

阅读更多

在项目中有一个需求,需要灵活配置调度任务时间,并能自由启动或停止调度。
有关调度的实现我就第一就想到了Quartz这个开源调度组件,因为很多项目使用过,Spring结合Quartz静态配置调度任务时间,非常easy。比如:每天凌晨几点定时运行一个程序,这只要在工程中的spring配置文件中配置好spring整合quartz的几个属性就好。

Spring配置文件

<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="simpleService" />
<property name="targetMethod" value="test" />
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 0/50 * ? * * *" />
</bean>
<bean  id="schedulerTrigger" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>     
</list>
</property>
</bean> 

 这种配置就是对quartz的一种简单的使用了,调度任务会在spring启动的时候加载到内存中,按照cronTrigger中定义的 cronExpression定义的时间按时触发调度任务。但是这是quartz使用“内存”方式的一种配置,也比较常见,当然对于不使用spring的项目,也可以单独整合quartz。方法也比较简单,可以从quartz的doc中找到配置方式,或者看一下《Quartz Job Scheduling Framework 》。

但是对于想持久化调度任务的状态,并且灵活调整调度时间的方式来说,上面的内存方式就不能满足要求了,正如本文开始我遇到的情况,需要采用数据库方式集成 Quartz,这部分集成其实在《Quartz Job Scheduling Framework 》中也有较为详细的介绍,当然doc文档中也有,但是缺乏和spring集成的实例。

一、需要构建Quartz数据库表,建表脚本在Quartz发行包的docs\dbTables目录,里面有各种数据库建表脚本,例子中采用的Quartz 2.2.1版本,使用H2内存数据库,执行了Quartz发行包的docs\dbTables\tables_h2.sql建表。

二、建立Maven project,完成后目录如下


 

三、配置applicationContext.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="   
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
   ">
    <context:component-scan base-package="com.sundoctor" />		
    <!-- 使用H2内存数据库并创建quartz数据库表 -->
    <jdbc:embedded-database id="dataSource" type="H2">
        <jdbc:script location="classpath:db/tables_h2.sql"/>       
    </jdbc:embedded-database>
</beans>

 
这里只是配置了数据库连接dataSource,为了简单方便,我使用H2内存数据库,执行tables_h2.sql创建quartz数据库表,还没有涉及到quartz有关配置,下面且听我慢慢道来。

四、实现动态定时任务
  什么是动态定时任务:是由客户制定生成的,服务端只知道该去执行什么任务,但任务的定时是不确定的(是由客户制定)。
这样总不能修改配置文件每定制个定时任务就增加一个trigger吧,即便允许客户修改配置文件,但总需要重新启动web服务啊,研究了下Quartz在Spring中的动态定时,发现

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 0/50 * ? * * *" />
</bean> 

 

cronExpression是关键,如果可以动态设置cronExpression的值,就可以顺利解决问题了。这样我们就不能直接使用org.springframework.scheduling.quartz.CronTriggerFactoryBean,需要自己实现一个动态调度服务类,在其中构建CronTrigger或SimpleTrigger,动态配置时间。
动态调度服务接口:

package com.sundoctor.quartz.service;

import java.util.Date;

import org.quartz.CronExpression;

public interface SchedulerService {
	/**
	 * 根据 Quartz Cron Expression 调试任务
	 * 
	 * @param cronExpression
	 *            Quartz Cron 表达式,如 "0/10 * * ? * * *"等
	 */
	void schedule(String cronExpression);

	/**
	 * 根据 Quartz Cron Expression 调试任务
	 * 
	 * @param name
	 *            Quartz CronTrigger名称
	 * @param cronExpression
	 *            Quartz Cron 表达式,如 "0/10 * * ? * * *"等
	 */
	void schedule(String name, String cronExpression);

	/**
	 * 根据 Quartz Cron Expression 调试任务
	 * 
	 * @param name
	 *            Quartz CronTrigger名称
	 * @param group
	 *            Quartz CronTrigger组
	 * @param cronExpression
	 *            Quartz Cron 表达式,如 "0/10 * * ? * * *"等
	 */
	void schedule(String name, String group, String cronExpression);

	/**
	 * 根据 Quartz Cron Expression 调试任务
	 * 
	 * @param cronExpression
	 *            Quartz CronExpression
	 */
	void schedule(CronExpression cronExpression);

	/**
	 * 根据 Quartz Cron Expression 调试任务
	 * 
	 * @param name
	 *            Quartz CronTrigger名称
	 * @param cronExpression
	 *            Quartz CronExpression
	 */
	void schedule(String name, CronExpression cronExpression);

	/**
	 * 根据 Quartz Cron Expression 调试任务
	 * 
	 * @param name
	 *            Quartz CronTrigger名称
	 * @param group
	 *            Quartz CronTrigger组
	 * @param cronExpression
	 *            Quartz CronExpression
	 */
	void schedule(String name, String group, CronExpression cronExpression);

	/**
	 * 在startTime时执行调试一次
	 * 
	 * @param startTime
	 *            调度开始时间
	 */
	void schedule(Date startTime);

	void schedule(Date startTime, String group);

	/**
	 * 在startTime时执行调试一次
	 * 
	 * @param name
	 *            Quartz SimpleTrigger 名称
	 * @param startTime
	 *            调度开始时间
	 */
	void schedule(String name, Date startTime);

	void schedule(String name, Date startTime, String group);

	/**
	 * 在startTime时执行调试,endTime结束执行调度
	 * 
	 * @param startTime
	 *            调度开始时间
	 * @param endTime
	 *            调度结束时间
	 */
	void schedule(Date startTime, Date endTime);

	void schedule(Date startTime, Date endTime, String group);

	/**
	 * 在startTime时执行调试,endTime结束执行调度
	 * 
	 * @param name
	 *            Quartz SimpleTrigger 名称
	 * @param startTime
	 *            调度开始时间
	 * @param endTime
	 *            调度结束时间
	 */
	void schedule(String name, Date startTime, Date endTime);

	void schedule(String name, Date startTime, Date endTime, String group);

	/**
	 * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次
	 * 
	 * @param startTime
	 *            调度开始时间
	 * @param repeatCount
	 *            重复执行次数
	 */
	void schedule(Date startTime, int repeatCount);

	/**
	 * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次
	 * 
	 * @param startTime
	 *            调度开始时间
	 * @param endTime
	 *            调度结束时间
	 * @param repeatCount
	 *            重复执行次数
	 */
	void schedule(Date startTime, Date endTime, int repeatCount);

	void schedule(Date startTime, Date endTime, int repeatCount, String group);

	/**
	 * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次
	 * 
	 * @param name
	 *            Quartz SimpleTrigger 名称
	 * @param startTime
	 *            调度开始时间
	 * @param endTime
	 *            调度结束时间
	 * @param repeatCount
	 *            重复执行次数
	 */
	void schedule(String name, Date startTime, Date endTime, int repeatCount);

	void schedule(String name, Date startTime, Date endTime, int repeatCount, String group);

	/**
	 * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次,每隔repeatInterval秒执行一次
	 * 
	 * @param startTime
	 *            调度开始时间
	 * 
	 * @param repeatCount
	 *            重复执行次数
	 * @param repeatInterval
	 *            执行时间隔间
	 */
	void schedule(Date startTime, int repeatCount, long repeatInterval);

	/**
	 * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次,每隔repeatInterval秒执行一次
	 * 
	 * @param startTime
	 *            调度开始时间
	 * @param endTime
	 *            调度结束时间
	 * @param repeatCount
	 *            重复执行次数
	 * @param repeatInterval
	 *            执行时间隔间
	 */
	void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval);

	void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval, String group);

	/**
	 * 在startTime时执行调试,endTime结束执行调度,重复执行repeatCount次,每隔repeatInterval秒执行一次
	 * 
	 * @param name
	 *            Quartz SimpleTrigger 名称
	 * @param startTime
	 *            调度开始时间
	 * @param endTime
	 *            调度结束时间
	 * @param repeatCount
	 *            重复执行次数
	 * @param repeatInterval
	 *            执行时间隔间
	 */
	void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval);

	void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval, String group);

	/**
	 * 暂停触发器
	 * 
	 * @param triggerName
	 *            触发器名称
	 */
	void pauseTrigger(String triggerName);

	/**
	 * 暂停触发器
	 * 
	 * @param triggerName
	 *            触发器名称
	 * @param group
	 *            触发器组
	 */
	void pauseTrigger(String triggerName, String group);

	/**
	 * 恢复触发器
	 * 
	 * @param triggerName
	 *            触发器名称
	 */
	void resumeTrigger(String triggerName);

	/**
	 * 恢复触发器
	 * 
	 * @param triggerName
	 *            触发器名称
	 * @param group
	 *            触发器组
	 */
	void resumeTrigger(String triggerName, String group);

	/**
	 * 删除触发器
	 * 
	 * @param triggerName
	 *            触发器名称
	 * @return
	 */
	boolean removeTrigdger(String triggerName);

	/**
	 * 删除触发器
	 * 
	 * @param triggerName
	 *            触发器名称
	 * @param group
	 *            触发器组
	 * @return
	 */
	boolean removeTrigdger(String triggerName, String group);
}

 

动态调度服务实现类:

package com.sundoctor.quartz.service;

import java.text.ParseException;
import java.util.Date;
import java.util.UUID;

import org.quartz.CronExpression;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("schedulerService")
public class SchedulerServiceImpl implements SchedulerService {

	private static final String NULLSTRING = null;
	private static final Date NULLDATE = null;

	@Autowired
	private Scheduler scheduler;
	@Autowired
	private JobDetail jobDetail;

	@Override
	public void schedule(String cronExpression) {
		schedule(NULLSTRING, cronExpression);
	}

	@Override
	public void schedule(String name, String cronExpression) {
		schedule(name, NULLSTRING, cronExpression);
	}

	@Override
	public void schedule(String name, String group, String cronExpression) {
		try {
			schedule(name, group, new CronExpression(cronExpression));
		} catch (ParseException e) {
			throw new IllegalArgumentException(e);
		}
	}

	@Override
	public void schedule(CronExpression cronExpression) {
		schedule(NULLSTRING, cronExpression);
	}

	@Override
	public void schedule(String name, CronExpression cronExpression) {
		schedule(name, NULLSTRING, cronExpression);
	}

	@Override
	public void schedule(String name, String group, CronExpression cronExpression) {

		if (isValidExpression(cronExpression)) {

			if (name == null || name.trim().equals("")) {
				name = UUID.randomUUID().toString();
			}

			CronTriggerImpl trigger = new CronTriggerImpl();
			trigger.setCronExpression(cronExpression);

			TriggerKey triggerKey = new TriggerKey(name, group);

			trigger.setJobName(jobDetail.getKey().getName());
			trigger.setKey(triggerKey);

			try {
				scheduler.addJob(jobDetail, true);
				if (scheduler.checkExists(triggerKey)) {
					scheduler.rescheduleJob(triggerKey, trigger);
				} else {
					scheduler.scheduleJob(trigger);
				}
			} catch (SchedulerException e) {
				throw new IllegalArgumentException(e);
			}
		}
	}

	@Override
	public void schedule(Date startTime) {
		schedule(startTime, NULLDATE);
	}

	@Override
	public void schedule(Date startTime, String group) {
		schedule(startTime, NULLDATE, group);
	}

	@Override
	public void schedule(String name, Date startTime) {
		schedule(name, startTime, NULLDATE);
	}

	@Override
	public void schedule(String name, Date startTime, String group) {
		schedule(name, startTime, NULLDATE, group);
	}

	@Override
	public void schedule(Date startTime, Date endTime) {
		schedule(startTime, endTime, 0);
	}

	@Override
	public void schedule(Date startTime, Date endTime, String group) {
		schedule(startTime, endTime, 0, group);
	}

	@Override
	public void schedule(String name, Date startTime, Date endTime) {
		schedule(name, startTime, endTime, 0);
	}

	@Override
	public void schedule(String name, Date startTime, Date endTime, String group) {
		schedule(name, startTime, endTime, 0, group);
	}

	@Override
	public void schedule(Date startTime, int repeatCount) {
		schedule(null, startTime, NULLDATE, 0);
	}

	@Override
	public void schedule(Date startTime, Date endTime, int repeatCount) {
		schedule(null, startTime, endTime, 0);
	}

	@Override
	public void schedule(Date startTime, Date endTime, int repeatCount, String group) {
		schedule(null, startTime, endTime, 0, group);
	}

	@Override
	public void schedule(String name, Date startTime, Date endTime, int repeatCount) {
		schedule(name, startTime, endTime, 0, 0L);
	}

	@Override
	public void schedule(String name, Date startTime, Date endTime, int repeatCount, String group) {
		schedule(name, startTime, endTime, 0, 0L, group);
	}

	@Override
	public void schedule(Date startTime, int repeatCount, long repeatInterval) {
		schedule(null, startTime, NULLDATE, repeatCount, repeatInterval);
	}

	@Override
	public void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval) {
		schedule(null, startTime, endTime, repeatCount, repeatInterval);
	}

	@Override
	public void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval, String group) {
		schedule(null, startTime, endTime, repeatCount, repeatInterval, group);
	}

	@Override
	public void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval) {
		schedule(name, startTime, endTime, repeatCount, repeatInterval, NULLSTRING);
	}

	@Override
	public void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval, String group) {

		if (this.isValidExpression(startTime)) {

			if (name == null || name.trim().equals("")) {
				name = UUID.randomUUID().toString();
			}

			TriggerKey triggerKey = new TriggerKey(name, group);

			SimpleTriggerImpl trigger = new SimpleTriggerImpl();
			trigger.setKey(triggerKey);
			trigger.setJobName(jobDetail.getKey().getName());

			trigger.setStartTime(startTime);
			trigger.setEndTime(endTime);
			trigger.setRepeatCount(repeatCount);
			trigger.setRepeatInterval(repeatInterval);

			try {
				scheduler.addJob(jobDetail, true);
				if (scheduler.checkExists(triggerKey)) {
					scheduler.rescheduleJob(triggerKey, trigger);
				} else {
					scheduler.scheduleJob(trigger);
				}
			} catch (SchedulerException e) {
				throw new IllegalArgumentException(e);
			}
		}
	}

	@Override
	public void pauseTrigger(String triggerName) {
		pauseTrigger(triggerName, NULLSTRING);
	}

	@Override
	public void pauseTrigger(String triggerName, String group) {
		try {
			scheduler.pauseTrigger(new TriggerKey(triggerName, group));// 停止触发器
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void resumeTrigger(String triggerName) {
		resumeTrigger(triggerName, NULLSTRING);
	}

	@Override
	public void resumeTrigger(String triggerName, String group) {
		try {
			scheduler.resumeTrigger(new TriggerKey(triggerName, group));// 重启触发器
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public boolean removeTrigdger(String triggerName) {
		return removeTrigdger(triggerName, NULLSTRING);
	}

	@Override
	public boolean removeTrigdger(String triggerName, String group) {
		TriggerKey triggerKey = new TriggerKey(triggerName, group);
		try {
			scheduler.pauseTrigger(triggerKey);// 停止触发器
			return scheduler.unscheduleJob(triggerKey);// 移除触发器
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	private boolean isValidExpression(final CronExpression cronExpression) {

		CronTriggerImpl trigger = new CronTriggerImpl();
		trigger.setCronExpression(cronExpression);

		Date date = trigger.computeFirstFireTime(null);

		return date != null && date.after(new Date());
	}

	private boolean isValidExpression(final Date startTime) {

		SimpleTriggerImpl trigger = new SimpleTriggerImpl();
		trigger.setStartTime(startTime);

		Date date = trigger.computeFirstFireTime(null);

		return date != null && date.after(new Date());
	}
}

 

SchedulerService 只有一个多态方法schedule,SchedulerServiceImpl实现SchedulerService接口,注入org.quartz.Schedulert和org.quartz.JobDetail,schedule方法可以动态配置org.quartz.CronExpression或org.quartz.SimpleTrigger调度时间。

五、实现自己的org.quartz.JobDetail

 

package com.sundoctor.example.service;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class MyQuartzJobBean extends QuartzJobBean {

	private static final Logger logger = LoggerFactory.getLogger(MyQuartzJobBean.class);

	@Override
	protected void executeInternal(JobExecutionContext jobexecutioncontext) throws JobExecutionException {
		Trigger trigger = jobexecutioncontext.getTrigger();
		String triggerName = trigger.getKey().getName();

		SimpleService simpleService = getApplicationContext(jobexecutioncontext).getBean("simpleService",
				SimpleService.class);
		simpleService.testMethod(triggerName);

	}

	private ApplicationContext getApplicationContext(final JobExecutionContext jobexecutioncontext) {
		try {
			return (ApplicationContext) jobexecutioncontext.getScheduler().getContext().get("applicationContextKey");
		} catch (SchedulerException e) {
			logger.error("jobexecutioncontext.getScheduler().getContext() error!", e);
			throw new RuntimeException(e);
		}
	}

}

 

MyQuartzJobBean继承org.springframework.scheduling.quartz.QuartzJobBean,SimpleService如下:

package com.sundoctor.example.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service("simpleService")
public class SimpleService {

	private static final Logger logger = LoggerFactory.getLogger(SimpleService.class);

	public void testMethod(String triggerName) {
		// 这里执行定时调度业务
		logger.info(triggerName);
	}

}


SimpleService主要执行定时调度业务,在这里我只是简单打印一下log日志。
配置applicationContext-quartz.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean name="quartzScheduler"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
		<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
		<property name="configLocation" value="classpath:quartz.properties" />
	</bean>

	<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
		<property name="jobClass">
			<value>com.sundoctor.example.service.MyQuartzJobBean</value>
		</property>	
		<property name="durability" value="true" />	
	</bean>

</beans>

 
dataSource:项目中用到的数据源,里面包含了quartz用到的数据库表;
applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下文以key/value的方式存放在了quartz的SchedulerContext,可以用applicationContextSchedulerContextKey所定义的key得到spring的ApplicationContext,然后就可使用ApplicationContext取得spring beans,使用方法参见MyQuartzJobBean;
configLocation:用于指明quartz的配置文件的位置,如果不用spring配置quartz的话,本身quartz是通过一个配置文件进行配置的,默认名称是quartz.properties,里面配置的参数在quartz的doc文档中都有介绍,可以调整quartz,我在项目中也用这个文件部分的配置了一些属性,代码如下:

org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

org.quartz.jobStore.misfireThreshold = 60000

#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.HSQLDBDelegate
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.useProperties = true
org.quartz.jobStore.tablePrefix = QRTZ_ 
org.quartz.jobStore.isClustered = false 
org.quartz.jobStore.maxMisfiresToHandleAtATime=1 

 


这里面没有数据源相关的配置部分,采用spring注入datasource的方式已经进行了配置。

六、测试
运行如下测试类

package com.sundoctor.example.test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.quartz.SchedulerException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.sundoctor.quartz.service.SchedulerService;

public class MainTest {

	/**
	 * @param args
	 * @throws SchedulerException
	 */
	public static void main(String[] args) throws SchedulerException {

		ApplicationContext springContext = new ClassPathXmlApplicationContext(new String[] {
				"classpath:applicationContext.xml", "classpath:applicationContext-quartz.xml" });
		SchedulerService schedulerService = springContext.getBean("schedulerService", SchedulerService.class);

		// 执行业务逻辑...

		// 设置高度任务
		// 每10秒中执行调试一次
		schedulerService.schedule("0/10 * * ? * * *");

		Date startTime = parse("2014-08-19 16:33:00");
		Date endTime = parse("2014-08-22 21:10:00");

		// 2014-08-19 16:33:00开始执行调度
		schedulerService.schedule(startTime);

		// 2014-08-19 16:33:00开始执行调度,2014-08-22 21:10:00结束执行调试
		schedulerService.schedule(startTime, endTime);

		// 2014-08-19 16:33:00开始执行调度,执行5次结束
		schedulerService.schedule(startTime, 5);

		// 2014-08-19 16:33:00开始执行调度,每隔20秒执行一次,执行5次结束
		schedulerService.schedule(startTime, 5, 20);

		// 等等,查看com.sundoctor.quartz.service.SchedulerService

	}

	private static Date parse(String dateStr) {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		try {
			return format.parse(dateStr);
		} catch (ParseException e) {
			throw new RuntimeException(e);
		}
	}

}

 
输出

[2014-08-19 22:31:50]INFO  com.sundoctor.example.service.SimpleService(line:14) -7e132a81-6d24-44e1-8d6c-15cdaeefb2ce
[2014-08-19 22:32:00]INFO  com.sundoctor.example.service.SimpleService(line:14) -7e132a81-6d24-44e1-8d6c-15cdaeefb2ce
[2014-08-19 22:32:10]INFO  com.sundoctor.example.service.SimpleService(line:14) -7e132a81-6d24-44e1-8d6c-15cdaeefb2ce
[2014-08-19 22:32:20]INFO  com.sundoctor.example.service.SimpleService(line:14) -7e132a81-6d24-44e1-8d6c-15cdaeefb2ce
[2014-08-19 22:32:30]INFO  com.sundoctor.example.service.SimpleService(line:14) -7e132a81-6d24-44e1-8d6c-15cdaeefb2ce
[2014-08-19 22:32:40]INFO  com.sundoctor.example.service.SimpleService(line:14) -7e132a81-6d24-44e1-8d6c-15cdaeefb2ce

 
这样只是简单的将quartz trigger名称打印出来。

这样通过SchedulerService就可以动态配置调度时间。其实SchedulerService 还可扩展,比如可以注入多个JobDetail,调度不同的JobDetail。

  • 大小: 56.6 KB
分享到:
评论
18 楼 sundoctor 2009-09-12  
badqiu 写道
lz只是需要cronExpression从数据库读取而己,不需要搞的这么复杂.


对于所有spring的${placeholder}值的读取,我扩展了一个JdbcPlaceholderConfigurer用于从数据库读取数据

通过数据库解析spring placeholder的值,配置示例
<bean id="propertyConfigurer" class="cn.org.rapid_framework.spring.beans.factory.config.JdbcPlaceholderConfigurer">
       <property name="dataSource" ref="dataSource"/>
       <property name="sql" value="select config_value,[default_value] from t_app_config where config_key = ?"/>
</bean>



http://www.rapid-framework.org.cn/rapid-javadoc-v2.0.x/cn/org/rapid_framework/spring/beans/factory/config/JdbcPlaceholderConfigurer.html


我觉得badqiu对此应用的理解有点偏差,我想要做的并不是简单的从数据库读取cronExpression,而是能够通过前端(比如Web页面)的修改并且不需要重启服务的情况下就可以动态修改配置任务调度时间,并且对于quartx的数据持久化是透明的,只需在数据库增加12张表,修改一下quartx.properties文件的配置,其它并不需要你做些什么额外的斯工作。

JdbcPlaceholderConfigurer继承自PropertyPlaceholderConfigurer
,将原来配置在一个properties文件中的内容转移到数据库而己。JdbcPlaceholderConfigurer只是应用启动时简单的将cronExpression从数据库读取出来,每次修改完数据库后就都需要重启服务,新的修改才会生效。其实JdbcPlaceholderConfigurer还是一种静态配置,只是将原来写在
引用
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 0/50 * ? * * *" />
</bean>

中的cronExpression写到另外一个地方:数据库。
17 楼 sundoctor 2009-09-12  
yanyu510 写道
lz问一下:
我要在simpleService里面注入一个继承HibernateDaoSupport的类,在testMethod()方法中执行数据库操作,spring注入为空,我想问一下,是不是因为用了quartz框架的原因,请lz尽快解答,感谢!


在simpleService里面注入一个继承HibernateDaoSupport的类,这个继承HibernateDaoSupport的类也必须实现序列化接口,simpleService类被序列化保存到数据库表 qrtz_job_details的job_class_name字段中,quartz在运行时会读取qrtz_job_details表中的 job_class_name将其反序列化。这也是为什么simpleService和其中注入各属性需要实现Serializable序列化接口的原因,所以你每次修改simpleService类或者其中的继承HibernateDaoSupport的类都要删除 qrtz_job_details表对应的job记录,否则可能会出现空指针异常,因为你如果你没有删除qrtz_job_details表中的记录,你修改的东东并不会自动更新到qrtz_job_details中,你用的还是原来旧版本的simpleService类。

你的这个问题在我另一篇文章《Quartz任务监控管理》有人提到过,你也可以到http://www.iteye.com/topic/441951?page=1看看。

16 楼 badqiu 2009-09-12  
lz只是需要cronExpression从数据库读取而己,不需要搞的这么复杂.


对于所有spring的${placeholder}值的读取,我扩展了一个JdbcPlaceholderConfigurer用于从数据库读取数据

通过数据库解析spring placeholder的值,配置示例
<bean id="propertyConfigurer" class="cn.org.rapid_framework.spring.beans.factory.config.JdbcPlaceholderConfigurer">
       <property name="dataSource" ref="dataSource"/>
       <property name="sql" value="select config_value,[default_value] from t_app_config where config_key = ?"/>
</bean>



http://www.rapid-framework.org.cn/rapid-javadoc-v2.0.x/cn/org/rapid_framework/spring/beans/factory/config/JdbcPlaceholderConfigurer.html
15 楼 yanyu510 2009-09-12  
lz问一下:
我要在simpleService里面注入一个继承HibernateDaoSupport的类,在testMethod()方法中执行数据库操作,spring注入为空,我想问一下,是不是因为用了quartz框架的原因,请lz尽快解答,感谢!
14 楼 mxdba321123 2009-09-04  
恩 开始我是想用list的 后来发现 匹配的问题
改用map后,大体思路和楼主是一样的 现在我只要稍加修改web前端 就可以实现指定任务用自定义的时间设置了,灵活了许多

再次感谢
13 楼 sundoctor 2009-09-04  
mxdba321123 写道
引用
其实SchedulerService 还可扩展,比如可以注入多个JobDetail,调度不同的JobDetail


请问 这个怎么实现呢?有没有什么思路呢,小弟我刚接触quartz不久,对quartz的结构不是很了解,但同时觉得多个 JobDeatail+动态时间配置 这2个都实现的话 就超级完美了

谢谢


首先实现多个JobDeatail并注册,比如:
引用

<bean id="jobDetail1" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>com.sundoctor.example.service.MyQuartzJobBean1</value>
</property>

<bean id="jobDetail2" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>com.sundoctor.example.service.MyQuartzJobBean2</value>
</property>


<bean id="jobDetail3" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>com.sundoctor.example.service.MyQuartzJobBean3</value>
</property>
...

其次将多个JobDeatail放到一个HashMap中
引用

<util:map id = "jobDeatailMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="org.springframework.scheduling.quartz.JobDetailBean">
<entry key="jobDetail1" ref="jobDetail1"/>
<entry key="jobDetail2" ref="jobDetail2"/>
<entry key="jobDetail3" ref="jobDetail3"/>
</util:map>


然后在SchedulerService 注入jobDeatailMap
@Service("schedulerService")  
public class SchedulerServiceImpl implements SchedulerService {  
   
     private Scheduler scheduler;  
     private Map<String,JobDetailBean> jobDeatailMap;  
   
     @Autowired  
     public void setJobDeatailMap(@Qualifier("jobDeatailMap") Map<String,JobDetailBean> jobDeatailMap) {  
         this.jobDeatailMap = jobDeatailMap;  
     }  
     @Autowired  
     public void setScheduler(@Qualifier("quartzScheduler") Scheduler scheduler) {  
         this.scheduler = scheduler;  
     } 
...


最后,修改SchedulerServiceImpl中的schedule方法,增加以jobDeatailMap KEY名字为参数:
     @Override  
     public void schedule(String jobDetailName,String name, CronExpression cronExpression) {  
         if (name == null || name.trim().equals("")) {  
             name = UUID.randomUUID().toString();  
         }  
   
         //这个时候JobDetail根据jobDetailName从jobDeatailMap获取
         JobDetail jobDetail = jobDeatailMap.get(jobDetailName);
         try {  
             scheduler.addJob(jobDetail, true);  
   
             CronTrigger cronTrigger = new CronTrigger(name, Scheduler.DEFAULT_GROUP, jobDetail.getName(),  
                     Scheduler.DEFAULT_GROUP);  
             cronTrigger.setCronExpression(cronExpression);  
             scheduler.scheduleJob(cronTrigger);  
             scheduler.rescheduleJob(name, Scheduler.DEFAULT_GROUP, cronTrigger);  
         } catch (SchedulerException e) {  
             throw new RuntimeException(e);  
         }  
     }
其它多态方法一样修改,增加jobDetailName参数。    


调用时,传不同的jobDetailName参数就可以调用不用的JobDetail。
SchedulerService schedulerService = (SchedulerService)springContext.getBean("schedulerService"); 

schedulerService.schedule("jobDetail1","审计任务","0/10 * * ? * * *");   

schedulerService.schedule("jobDetail2","发放任务","0/10 * * ? * * *"); 

schedulerService.schedule("jobDetail3","AAA任务","0/10 * * ? * * *"); 
12 楼 mxdba321123 2009-09-04  
引用
其实SchedulerService 还可扩展,比如可以注入多个JobDetail,调度不同的JobDetail


请问 这个怎么实现呢?有没有什么思路呢,小弟我刚接触quartz不久,对quartz的结构不是很了解,但同时觉得多个 JobDeatail+动态时间配置 这2个都实现的话 就超级完美了

谢谢
11 楼 sundoctor 2009-09-03  
spyker 写道
sundoctor 写道
trigger的保存是quartz自己本身实现的,跟jquery没有一点关系,jquery只是前台的ajax调用,当我们调用Scheduler.scheduleJob(Trigger trigger)增加一个Trigger时quartz自己就会往库里插入数据了,对于Trigger数据的增删改都quartz本身持久化支持的,我们不用关心。
添加一个 simple Trigger时往qrtz_simple_triggers写一条记录,同时也会往qrtz_triggers写入一条记录,两个表根据trigger_name、trigger_group对应,开始时间存在qrtz_triggers表中,还有结束时间、上次执行时间、下次执行时间等都存在于qrtz_triggers表中。添加一个 cron Trigger时也一样,同时往qrtz_cron_triggers、qrtz_triggers各写入一条记录。

这个进入数据库的 需要在quartz中做相对应配置
具体的配置 好久不记得了 你可以参考quartz配置文档



具体的配置就是将
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
换成
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
就会自动入库了
10 楼 spyker 2009-09-02  
sundoctor 写道
trigger的保存是quartz自己本身实现的,跟jquery没有一点关系,jquery只是前台的ajax调用,当我们调用Scheduler.scheduleJob(Trigger trigger)增加一个Trigger时quartz自己就会往库里插入数据了,对于Trigger数据的增删改都quartz本身持久化支持的,我们不用关心。
添加一个 simple Trigger时往qrtz_simple_triggers写一条记录,同时也会往qrtz_triggers写入一条记录,两个表根据trigger_name、trigger_group对应,开始时间存在qrtz_triggers表中,还有结束时间、上次执行时间、下次执行时间等都存在于qrtz_triggers表中。添加一个 cron Trigger时也一样,同时往qrtz_cron_triggers、qrtz_triggers各写入一条记录。

这个进入数据库的 需要在quartz中做相对应配置
具体的配置 好久不记得了 你可以参考quartz配置文档

9 楼 giantleap 2009-09-01  
正在看有关的资料,这个写的真好,大赞
8 楼 jayxu 2009-09-01  
搞的真复杂!
7 楼 iRoger 2009-08-14  
好文章,研究中
6 楼 sundoctor 2009-08-09  
trigger的保存是quartz自己本身实现的,跟jquery没有一点关系,jquery只是前台的ajax调用,当我们调用Scheduler.scheduleJob(Trigger trigger)增加一个Trigger时quartz自己就会往库里插入数据了,对于Trigger数据的增删改都quartz本身持久化支持的,我们不用关心。
添加一个 simple Trigger时往qrtz_simple_triggers写一条记录,同时也会往qrtz_triggers写入一条记录,两个表根据trigger_name、trigger_group对应,开始时间存在qrtz_triggers表中,还有结束时间、上次执行时间、下次执行时间等都存在于qrtz_triggers表中。添加一个 cron Trigger时也一样,同时往qrtz_cron_triggers、qrtz_triggers各写入一条记录。
5 楼 derpvail 2009-08-09  
请问一下,添加trigger是怎么保存在数据库中的?因为我没看见你的dao中有保存的方法。是不是jquery实现的。。。。还真不会jquery。。。。
还有一个问题请教,qrtz_simple_triggers表中的开始时间存在哪里了?我只定义了开始时间,可是这个开始时间存在哪里了?期待解答,谢谢!
4 楼 derpvail 2009-08-09  
您的《Quartz任务监控管理》我已经完全看懂了,和我想要的完全一样,只是页面的表现形式不一样。
太感谢了!呵呵!网上关于quartz的文章一大堆,没几个正确的,而且都很肤浅。您的文章是个例外!
我的CMS系统定时发布模块写好以后,一定要把针对您发表的技术做做修改,也写一篇正确详细的blog来。
这样大家就会少走弯路了!
再次感谢您!
3 楼 sundoctor 2009-08-06  
derpvail 写道
SchedulerService schedulerService = (SchedulerService)springContext.getBean("schedulerService");  
请问您这个schedulerService在spring的配置文件中是怎么配置的?我用的spring2.0
不通过注解的方式,而是配置spring中,该如何?我配置在spring中调用的时候就报null的错。另外,您的例子我下载看了,有两个测试类。感觉差不多。。。看了有些迷糊。。。。我目前也正遇到这样的问题,请您帮忙!多谢了!

不用注解应该也没什么问题了,只是配置方式不一样的而己,采用xml配置schedulerService时将其中的scheduler、jobDetail要注入,先要修改一g下SchedulerServiceImpl去掉所有注解:@Service("schedulerService") 、@Autowired 、@Qualifier("jobDetail")、@Qualifier("quartzScheduler")
然后在配置文件添加

<bean id="schedulerService" class="com.sundoctor.quartz.service.SchedulerServiceImpl">
<property name="scheduler" ref="quartzScheduler" />
<property name="jobDetail" ref="jobDetail" />
</bean>


两个测试类没有什么区别,一个采用了spring测试框架,Junit测试方式,另一个是普通main函数测试方式
2 楼 derpvail 2009-08-05  
SchedulerService schedulerService = (SchedulerService)springContext.getBean("schedulerService");  
请问您这个schedulerService在spring的配置文件中是怎么配置的?我用的spring2.0
不通过注解的方式,而是配置spring中,该如何?我配置在spring中调用的时候就报null的错。另外,您的例子我下载看了,有两个测试类。感觉差不多。。。看了有些迷糊。。。。我目前也正遇到这样的问题,请您帮忙!多谢了!
1 楼 xieboxin 2009-06-02  
非常感谢!!

之前都要写一个动态配置调度时间的,查了很久都找不到解决方法。谢谢

相关推荐

    Spring Quartz如何动态配置时间

    在本文中,我们将讨论如何使用 Spring Quartz 实现动态配置时间,并提供了详细的实现步骤和实践经验。 动态配置时间的目的 在实际应用中,任务的执行时间往往需要根据业务需求进行动态调整,以满足不同的需求场景...

    quartz整合springbatch动态集群定时实现mysql参考

    本人花费多日时间,亲测有效。所需文件代码完整,只需导入常用开发IDE和mysql数据库即可正常使用。

    Spring中的Quartz配置-Spring-定时器-java定时器.doc

    在本篇文章中,我们将讨论如何在 Spring 中配置 Quartz,以实现 Java 定时器的功能。 Quartz 介绍 Quartz 是一个开源的作业调度器,可以让开发者轻松地实现任务的定时执行。它提供了强大的调度功能,可以满足复杂...

    关于spring中quartz的配置

    在Spring配置文件(如`applicationContext.xml`)中,定义`SchedulerFactoryBean`来实例化和配置Quartz Scheduler: ```xml &lt;bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz....

    spring整合java quartz实现动态定时任务的前台网页配置与管理

    在实际项目应用中经常会用到定时任务,可以通过quartz和spring的简单配置即可完成,但如果要改变任务的执行时间、频率,废弃任务等就需要改变配置甚至代码需要重启服务器,这里介绍一下如何通过quartz与spring的组合...

    Quartz在Spring中动态设置cronExpression

    本文将详细解释如何在Spring中动态设置Quartz的`cronExpression`,以实现灵活的定时任务配置。 首先,让我们了解什么是动态定时任务。动态定时任务是指任务的执行时间不是固定的,而是根据用户需求或者外部条件来...

    Spring Quartz动态配置时间.rar

    Spring Quartz动态配置时间.rar

    Quartz+Spring定时触发器例子

    - 如何在Spring中配置Quartz - 如何创建和配置JobDetail和Trigger - Cron表达式的使用和理解 - 如何在Job类中注入依赖 - 如何启动和停止Scheduler - 如何调试和管理定时任务 总的来说,"Quartz+Spring定时触发器...

    spring2与quartz在Web整合

    3. **整合过程**:整合 Spring 2 和 Quartz 需要在 Spring 配置文件中声明 Job 和 Trigger 的 Bean,并指定它们的关联。Spring 可以通过其自身的 JobFactory 来创建 Job 实例,使得 Job 可以利用 Spring 的 DI 功能...

    Quartz + spring简单配置多个任务调度

    Quartz + spring简单配置多个任务调度 spring配置文件 简单配置

    Quartz + spring的开发步骤

    Quartz 是一个开源的作业调度...在 Spring 的配置文件(如 `applicationContext.xml`)中,我们需要配置 Quartz 的 SchedulerFactoryBean。这将创建一个 Quartz Scheduler 实例,并将其注入到 Spring 容器中。 ```xml ...

    spring+quartz demo

    Spring 提供了与 Quartz 集成的模块,使得在 Spring 应用程序中使用 Quartz 更加便捷,包括 Job 实例的管理、触发器的配置以及对 Quartz Scheduler 的控制。 在这个 "spring+quartz demo" 中,我们可以学习到如何将...

    quartz在spring中的使用实例

    2. **配置Quartz**:在Spring的配置文件(如applicationContext.xml)中,声明`SchedulerFactoryBean`,这是Spring管理Quartz Scheduler的主要方式: ```xml &lt;bean id="scheduler" class="org.springframework....

    spring quartz 时间调用框架

    spring框架配置的时间调度插件,提供jar包 及 配置实例

    quartz spring

    在Spring的配置文件(如applicationContext.xml)中,你可以定义一个`SchedulerFactoryBean`,这个bean负责创建和管理Quartz的Scheduler实例。在这里,你可以设置Scheduler的一些属性,比如线程池大小、是否启动...

    spring的quartz使用实例

    - 创建SchedulerFactoryBean:在Spring配置文件中定义一个`SchedulerFactoryBean`,设置相关属性,如JobStore类型、ThreadPool大小等。 - 注册JobDetail和Trigger:创建`JobDetail`对象表示Job,设置Job类、名字和...

    Spring线程时间配置:Quartz和CronExpression

    Spring自带了多线程设置:quartz,在quartz的设置中,时间字段CronExpression的设置比较纠结,一个Cron表达式是一个由六至七个字段组成由空格分隔的字符串,其中6个字段是必须的而一个是可选的,文档详细讲解了这个...

    Quartz+spring实例应用

    总的来说,通过这个实例应用,开发者可以学习到如何在Spring环境中利用Quartz进行任务调度,理解Spring如何管理Quartz的作业和触发器,以及如何编写和配置作业类。这些知识对于任何需要进行定时任务处理的Java项目都...

Global site tag (gtag.js) - Google Analytics