spring&quartz的项目应用总结
一.名词简介:
Quartz是一个完全由java编写的开源作业调度框架。Spring为创建Quartz的Scheduler、Trigger和JobDetail提供了便利的FactoryBean类,以便能够在 Spring容器中享受注入的好处。此外Spring还提供了一些便利工具类直接将Spring中的Bean包装成合法的任务。Spring进一步降低了使用Quartz的难度,能以更具Spring风格的方式使用Quartz。概括来说它提供了两方面的支持:
1)为Quartz的重要组件类提供更具Bean风格的扩展类;
2)提供创建Scheduler的BeanFactory类,方便在Spring环境下创建对应的组件对象,并结合Spring容器生命周期进行启动和停止的动作。
二。项目实践
情景一:集群部署,持久化,动态创建schedule配置
1.spring-schedule.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="byName">
<bean id="GMTZone" factory-method="getTimeZone">
<constructor-arg value="GMT"/>
</bean>
<!--业务job生成工厂,该工厂注入了scheduleManager -->
<bean id="pharmaskyJobFactory" style="padding: 0pt; background-color: yellow; display: inline; font-size: inherit; color: black;">PharmaskyJobFactory"/>
<!--schedule管理器,用于动态添加,修改,删除schedule -->
<bean id="scheduleManager" ref="scheduleFactoryBean"/>
</bean>
<!--持久化,集群schedule工厂配置信息,实际可以在classpath下附加quartz.properties定义这些属性值 -->
<bean id="scheduleFactoryBean" ref="dataSource"/>
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">PharmaskyClusteredScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">25</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<propkey="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
</props>
</property>
<property name="triggers">
<list>
<ref local="orderConversionCronTrigger"/>
</list>
</property>
</bean>
<!--固定jobDetail, org.quartz.JobDetail类型的JobDetail必须实现指定接口方法来执行job-->
<bean id="autoOrderStatusConversionJob" value="PMS-SYS-JOB-HIS-ORDER-CONVERSION"/>
<property name="group" value="PMS-GROUP"/>
<property name="requestsRecovery" value="true"/>
</bean>
<!--job触发器,绑定上面的jobDetail -->
<bean id="orderConversionCronTrigger" value="PMS-SYS-JOB-HIS-ORDER-CONVERSION-Trigger"/>
<property name="jobDetail" ref="autoOrderStatusConversionJob" />
<property name="cronExpression" value="0 0/20 * * * ?" />
<property name="timeZone" ref="GMTZone"/>
</bean>
</beans>
2.ScheduleManager.java动态管理schedule工具类
<!-- <br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->
packagecom.infowarelab.pharmasky.service.schedule;
importjava.text.ParseException;
importjava.util.ArrayList;
importjava.util.Date;
importjava.util.List;
importorg.apache.commons.lang.StringUtils;
importorg.quartz.CronTrigger;
importorg.quartz.JobDetail;
importorg.quartz.Scheduler;
importorg.quartz.SchedulerException;
importorg.quartz.Trigger;
importcom.infowarelab.common.log.Log;
importcom.infowarelab.common.log.LogFactory;
importcom.infowarelab.pharmasky.util.Config;

publicclassScheduleManager
{
privatestaticfinalLoglog=LogFactory.getLog(ScheduleManager.class);
privateSchedulerscheduler;
publicvoidstart()throwsSchedulerException
{
if(scheduler==null)
{
scheduler=getScheduler();
}
if(scheduler.isShutdown()||scheduler.isInStandbyMode())
{
scheduler.start();
}
}
publicvoidpause()throwsSchedulerException
{
if(scheduler==null)
{
scheduler=getScheduler();
}
if(scheduler.isStarted())
{
scheduler.standby();
}
}
publicvoidstop()throwsSchedulerException
{
if(scheduler==null)
{
scheduler=getScheduler();
}
if(scheduler.isStarted()||scheduler.isInStandbyMode())
{
scheduler.shutdown();
}
}
publicListlistJobDetails()throwsSchedulerException
{
ListrstList=newArrayList<JobDetail>();
if(scheduler==null)
{
scheduler=getScheduler();
}
String[]groupNames=scheduler.getJobGroupNames();
log.info("[INFO]thegroupsincurrentscheduleris:"
+groupNames.length);
for(inti=0;i<groupNames.length;i++)
{
String[]jobNames=scheduler.getJobNames(groupNames[i]);
for(intj=0;j<jobNames.length;j++)
{
JobDetailjobDetail=scheduler.getJobDetail(jobNames[j],
groupNames[i]);
if(jobDetail!=null)
{
rstList.add(jobDetail);
}
}
}
returnrstList;
}
publicJobDetailgetJobDetail(StringjobName,StringjobGroupName)
throwsSchedulerException
{
if(scheduler==null)
{
scheduler=getScheduler();
}
JobDetailjob=scheduler.getJobDetail(jobName,jobGroupName);
if(job==null)
{
log.info("[INFO]noneobjectrelativethejobName:"+jobName
+"andjobGroupName:"+jobGroupName);
}
returnjob;
}
publicList<JobDetail>getJobDetailByGroup(StringjobGroupName)
throwsSchedulerException
{
List<JobDetail>list=newArrayList<JobDetail>();
if(scheduler==null)
{
scheduler=getScheduler();
}
String[]jobNames=scheduler.getJobNames(jobGroupName);
log.debug("[DEBUG]jobNameswith/""+jobGroupName
+"/"asgroupName:"+jobNames);
for(inti=0;i<jobNames.length;i++)
{
JobDetailjobDetail=getJobDetail(jobNames[i],jobGroupName);
if(jobDetail!=null)
{
list.add(jobDetail);
}else
{
log
.debug("[DEBUG]JobDetailwith{/""
+jobNames[i]
+"/",/""
+jobGroupName
+"/"}asthejobNameandjobGroupNamecannotbefound!");
}
}
returnlist;
}
publicbooleanaddJobDetail(JobDetailjobDetail,StringcronExpress)
throwsSchedulerException,ParseException
{
if(scheduler==null)
{
scheduler=getScheduler();
}
if(jobDetail!=null&&cronExpress!=null)
{
Triggertrigger=newCronTrigger(jobDetail.getName(),jobDetail.getGroup(),
jobDetail.getName(),jobDetail.getGroup(),cronExpress);
DatejobDate=scheduler.scheduleJob(jobDetail,trigger);
log
.info("[INFO]jobDetail:"
+jobDetail.getFullName()
+"hasbeenaddedintothescheduler,andthefirstFiredTimeis:"
+jobDate);
return(jobDate==null)?false:true;
}else
{
log
.info("[INFO]addjobDetailfailure,theparametersonjobDetailortriggerhasnullvalue!");
}
returnfalse;
}
publicTrigger[]getJobTrigger(StringjobName,StringjobGroupName)
throwsSchedulerException
{
if(scheduler==null)
{
scheduler=getScheduler();
}
returnscheduler.getTriggersOfJob(jobName,jobGroupName);
}
publicList<Trigger>listTrigger()throwsSchedulerException
{
List<Trigger>triggerList=newArrayList<Trigger>();
if(scheduler==null)
{
scheduler=getScheduler();
}
String[]triggerGroupNames=scheduler.getTriggerGroupNames();
log
.info("[INFO]thetriggergroupsamountswhichisregistedwithinthesystemis:"
+triggerGroupNames.length);
for(StringtriggerGroupName:triggerGroupNames)
{
if(triggerGroupName!=null)
{
String[]triggerNames=scheduler
.getTriggerNames(triggerGroupName);
for(StringtriggerName:triggerNames)
{
Triggertrigger=scheduler.getTrigger(triggerName,
triggerGroupName);
if(trigger!=null
&&scheduler.getTriggerState(triggerName,
triggerGroupName)==Trigger.STATE_NORMAL)
{
triggerList.add(trigger);
}else
{
log.info("[INFO]thetrigger:"
+((trigger==null)?trigger.getFullName()
:"null")+",notexists"
+"orhasthenotnormalstatus");
}
}
}
}
returntriggerList;
}
publicbooleanreScheduleJob(StringjobName,StringjobGroupName,
StringcomputedStr)throwsSchedulerException,ParseException
{
if(scheduler==null)
{
scheduler=getScheduler();
}
TriggernewTrigger=newCronTrigger(jobName,jobGroupName,jobName,
jobGroupName,computedStr);
newTrigger.setJobName(jobName);
newTrigger.setJobGroup(jobGroupName);

DatetriggerDate=scheduler.rescheduleJob(jobName,jobGroupName,newTrigger);
log.info("[INFO]thejob"+jobName
+"hasbeenupdatetofiredon:"+triggerDate);
return(triggerDate==null)?false:true;
}
publicStringgenScheduleTimeStr(bytetype,Short
scheduleTimeParams)
{
Stringpattern=null;//variableforcronexpress
switch(type)
{
case1:
//autoordercreationjob,theparameterturnis:weekInterval,
//weekday,hour,minute
//"0minutehour?*weekDay/weekInterval"
pattern=Config
.getString("schedule.cron.express.his.order.creation");
log.info("[INFO]partternforhisordercreationcronexpress:"
+pattern);
for(inti=0;i<scheduleTimeParams.length;i++)
{
switch(i)
{
case0://minute
pattern=StringUtils.replace(pattern,"minute",
scheduleTimeParams[0].toString());
break;
case1://hour
pattern=StringUtils.replace(pattern,"hour",
scheduleTimeParams[1].toString());
break;
case2://weekDay
pattern=StringUtils.replace(pattern,"weekDay",
getWeekIdentify(scheduleTimeParams[2]));
break;
case3://weekInterval
pattern=StringUtils.replace(pattern,"weekInterval",
scheduleTimeParams[3].toString());
break;
default:
break;
}
}
break;
case2:
//hisstocksyncjob,theparameterturnis:dayInterval,hour,
//minute
//"0minutehour*/dayInterval*?"
pattern=Config.getString("schedule.cron.express.his.stock.sync");
log.info("[INFO]partternforhisordercreationcronexpress:"
+pattern);
for(inti=0;i<scheduleTimeParams.length;i++)
{
switch(i)
{
case0://minute
pattern=StringUtils.replace(pattern,"minute",
scheduleTimeParams[0].toString());
break;
case1://hour
pattern=StringUtils.replace(pattern,"hour",
scheduleTimeParams[1].toString());
break;
case2://dayInterval
pattern=StringUtils.replace(pattern,"dayInterval",
scheduleTimeParams[2].toString());
break;
default:
break;
}
}
break;
default:
break;
}
log.info("[INFO]schedulejobcronexpressis:"+pattern);
return(pattern==null)?null:pattern.trim();
}
/***//*************************************************************************/
/***//**helpermethodorgettersandsetters**/
/***//*************************************************************************/
/***//**
*gettheweekday'sidentifylike"SUN","MON"andsoon.Inthismethod
*thedata"1"equals"SUN",sothe"7"equals"SAT";
*
*@paramshort
*thenumericdataforweekday
*@returnStringtheweekdayidentifywithcorrectnumericdata
*/
privateStringgetWeekIdentify(shortweekDay)
{
Stringvalue=null;
switch(weekDay)
{
case1:
value="SUN";
break;
case2:
value="MON";
break;
case3:
value="TUE";
break;
case4:
value="WED";
break;
case5:
value="THU";
break;
case6:
value="FRI";
break;
case7:
value="SAT";
break;
default:
break;
}
returnvalue;
}
publicSchedulergetScheduler()
{
returnscheduler;
}
publicvoidsetScheduler(Schedulerscheduler)
{
this.scheduler=scheduler;
}
}
?
情景二:单机部署,固定触发规则多个schedule,不用持久化
1.spring-schedule.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="byName">
<!-- Job Bean -->
<bean name="autoOrderAuditService" autowire="no">
<property name="tasks">
<list>
<ref bean="autoOrderAuditService"/>
<ref bean="sendAuditedOrderService"/>
</list>
</property>
<property name="samBaseInfoService" ref="samBaseInfoService"></property>
</bean>
<bean id="timerJobDetail" autowire="no">
<property name="targetObject" ref="timerJobManagerJob" />
<property name="targetMethod" value="jobExpires" />
</bean>
<bean id="timerCronTrigger" autowire="no">
<property name="jobDetail" ref="timerJobDetail" />
<property name="cronExpression">
<value>0 0/15 * * * ?</value>
</property>
</bean>
<bean autowire="no">
<property name="triggers">
<list>
<ref bean="timerCronTrigger" />
</list>
</property>
</bean>
</beans>
?
参考资料:
cronExpression表达式:
http://hi.baidu.com/the_space_of_programmer/blog/item/725759f78d383e27720eec80.html (cronExpression
quartz介绍:
http://hi.baidu.com/alliance_alex/blog/item/5c998d64241173f2f73654b0.html
集群及quartz配置说明:
http://hi.baidu.com/jiangyou001/blog/item/5196d990862d5789a977a4af.html
http://gocom.primeton.com/modules/newbb/forumtopic19180_9963_40.htm
publicclassScheduleManager
publicvoidstart()throwsSchedulerException