spring 任务调度总结
参考资料
http://www.ibm.com/developerworks/cn/java/j-quartz/
http://www.opensymphony.com/quartz/download.action
Java的Timer类和OpenSymphony的Quartz调度器是两个流行的调度API。Spring为这两个调度器提供了一个抽象层,可以更容易的使用他们
spring+timer
1 .使用java Timer调度任务
第一步 web.xml
<!--*********************** spring setting ***********************-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContextTimeTask.xml
</param-value>
</context-param>
第二步 配置applicationContextTimeTask.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--
****************使用java Timer调度任务*********************
使用java Timer调度第一步是从java.util.TimerTask派生一个任务
声明本身只是将EmailReportTask放到应用上下文,
并在testBean属性装配TestBean
在调度之前不会做任何事。
-->
<bean id="testBean" class="com.photo.test.TestBean"/>
<bean id="reportTimerTask" class="com.photo.task.EmailReportTask">
<property name="testBean">
<ref bean="testBean"/>
</property>
</bean>
<!--
Spring的ScheduledTimerTimerTask定义了一个定时器任务的运行周期。应该如下装配一个ScheduledTimerTask
timerTask告诉ScheduledTimerTask运行哪个TimerTask,
这里该装配属性指向reportTimerTask的一个引用。
属性period告诉ScheduledTimerTask以怎样的频率调用TimerTask的run()方法。
这个属性以毫秒作为单位,
86400000指定该任务每24小时运行一次
10X1000
属性scheduledTimerTasks 要求一个需要启动的定时器任务列表。
ScheduledTimerTask有一个delay属性, 允许你指定当任务第一次运行之前应该等多久。
如,EmailReportTask的第一次运行延迟5毫秒
-->
<bean id="scheduledReportTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="timerTask">
<ref bean="reportTimerTask"/>
</property>
<property name="period">
<value>10000</value>
</property>
<property name="delay">
<value>5000</value>
</property>
</bean>
<!--Spring 的TimerFactoryBean负责启动定时任务。按以下方式在Spring配置文件里声明它-->
<bean class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref bean="scheduledReportTask"/>
</list>
</property>
</bean>
</beans>
第三步 相关类的编写
EmailReportTask.java
public class EmailReportTask extends TimerTask {//必需继承TimerTask
Log log= LogFactory.getLog(EmailReportTask.class);
public EmailReportTask() {
}
public void run() {//这个函数是调度任务执行的入口
log.debug("task begin :"+testBean.greeting);
}
private TestBean testBean;//这里实现spring的依赖注入
public void setTestBean(TestBean testBean) {
this.testBean = testBean;
}
}
testBean.java
public class TestBean {
public String greeting="hello,world";
}
spring+Quartz
Quartz 作业调度框架所提供的 API 在两方面都表现极佳:既全面强大,又易于使用。Quartz 可以用于简单的作业触发,也可以用于复杂的 JDBC 持久的作业存储和执行。
第一步 同上
第二步 配置applicationContextTimeTask.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="reportJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>com.photo.task.EmailReportTask2</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="testBean">
<ref bean="testBean"/>
</entry>
</map>
</property>
</bean>
<!--用它来指定一个工作应以怎样的频率运行,及第一次运行工作前应等多久-->
<bean id="simpleReportTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail">
<ref bean="reportJob"/>
</property>
<property name="startDelay">
<value>3600000</value>
</property>
<property name="repeatInterval">
<value>86400000</value>
</property>
</bean>
<!--
CronTriggerBean允许更精确的控制任务的运行时间。如果要在每天的6:00运行任务可以按照以下方式声明一个CronTriggerBean
0 0 6 * * ?
jobDetail属性告诉触发器调度哪一个工作,属性cronExpression告诉触发器何时触发。一个cron表达式至少有6个由空格分隔的时间元素(最多7个)从左到右元素定义如下
1。秒(0-59)
2。分(0-59)
3。小时(0-23)
4。月份中的日期(1-31)
5。月份(1-12 或 JAN-DEC)
6。星期(1-7 或 SUN-SAT)
7。年份(1970-2099)
每一个元素都可以显示规定的一个值,一个区间(9-14),一个列表(9,12,14)或一个通配符(*)月份中的日期和星期中的日期这两个元素时互斥的一起应该通过设置一个问号(?)来表明不想设置那个字段
对于cronReportTrigger我们设置的cronExpression为 0 0 6 * * ?可以读作任意月份任何日期的6时0分0秒执行触发器
-->
<bean id="cronReportTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="reportJob"/>
</property>
<property name="cronExpression">
<value>0/5 * * * * ?</value>
</property>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronReportTrigger"/>
</list>
</property>
</bean>
</beans>
第三步 相关类的编写
EmailReportTask2.java
public class EmailReportTask2 extends QuartzJobBean {
Log log = LogFactory.getLog(EmailReportTask.class);
public EmailReportTask2() {
}
//这个函数是调度任务执行的入口
protected void executeInternal(JobExecutionContext jobExecutionContext) throws org.quartz.JobExecutionException {
log.debug("task 2 begin :" + testBean.greeting);
}
private TestBean testBean;//这里实现spring的依赖注入
public void setTestBean(TestBean testBean) {
this.testBean = testBean;
}
}
TestBean.java 同上
以上转自javaeye的某位朋友博客
以下引用自javaeye robbin:
如果是我的话,我采取的办法就是自己单独启动一个Job Server,来跑job,不会部署在web容器中。
其他web节点当需要启动异步任务的时候,可以通过种种方式(DB, JMS, Web Service, etc)通知Job Server,而Job Server收到这个通知之后,把异步任务加载到自己的任务队列中去。
其实想改造当前已经集成quartz的web应用也不算困难:
例如可以使用数据库的表来记录和维护任务队列和状态,把quartz部分完全从web应用中剥离出去,自己写一个Java Main程序把配置quartz的spring容器跑起来,这样Job Server就启动了(注意这个Job Server完全脱离tomcat)。此外这个Main程序应该再启动一个子线程,定期扫描数据库的任务队列表:
有新的任务就加入quartz的任务调度;
把当前任务的执行状态写入任务表;
看到删除任务的表字段状态以后,删除相应的任务。
然后web应用去掉quartz部分配置,把原来的调用quartz任务的代码改写为读写数据库的任务表,这样就把job部分完全从web容器剥离掉了,甚至web容器做cluster也没有问题了,并且多个web节点在同时读写任务表的时候,还有数据库的事务来确保操作的一致性,实在是很棒。
另外还可以单独做一个job管理界面,可以通过web界面手工添加任务,查看任务状态,删除任务等等。