java statefuljob_Spring+quartz 实现定时任务job集群配置【原】

本文介绍了如何使用Spring和Quartz框架实现可序列化的StatefulJob,以支持集群环境下的定时任务配置。文章通过提供一个名为MethodInvokingJobDetailFactoryBean的FactoryBean,实现了在不持久化MethodInvoker的情况下,调用指定类或对象的方法。文中给出了详细的配置和使用示例,展示了如何设置并发、持久性、挥发性和恢复等属性,以及如何添加Job监听器。
摘要由CSDN通过智能技术生成

packageking.springframework.scheduling.quartz;importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;importorg.quartz.Job;importorg.quartz.JobDetail;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;importorg.quartz.Scheduler;importorg.quartz.StatefulJob;importorg.springframework.beans.factory.BeanNameAware;importorg.springframework.beans.factory.FactoryBean;importorg.springframework.beans.factory.InitializingBean;importorg.springframework.context.ApplicationContext;importorg.springframework.util.MethodInvoker;/*** 该类可让生成的job可以序列化到数据库中,Spring自带的MethodInvokingJobDetailFactoryBean却不能序列化生成的job

* This is a cluster safe Quartz/Spring FactoryBean implementation, which produces a JobDetail implementation that can invoke any no-arg method on any Class.

*

* Use this Class instead of the MethodInvokingJobDetailBeanFactory Class provided by Spring when deploying to a web environment like Tomcat.

*

* Implementation

* Instead of associating a MethodInvoker with a JobDetail or a Trigger object, like Spring's MethodInvokingJobDetailFactoryBean does, I made the [Stateful]MethodInvokingJob, which is not persisted in the database, create the MethodInvoker when the [Stateful]MethodInvokingJob is created and executed.

*

* A method can be invoked one of several ways:

*

*

The name of the Class to invoke (targetClass) and the static method to invoke (targetMethod) can be specified.

*

The Object to invoke (targetObject) and the static or instance method to invoke (targetMethod) can be specified (the targetObject must be Serializable when concurrent=="false").

*

The Class and static Method to invoke can be specified in one property (staticMethod). example: staticMethod = "example.ExampleClass.someStaticMethod"

* Note: An Object[] of method arguments can be specified (arguments), but the Objects must be Serializable if concurrent=="false".

*

*

* I wrote MethodInvokingJobDetailFactoryBean, because Spring's MethodInvokingJobDetailFactoryBean does not produce Serializable

* JobDetail objects, and as a result cannot be deployed into a clustered environment like Tomcat (as is documented within the Class).

*

* Example

*

*

* <bean id="exampleTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">

*

<!-- Execute example.ExampleImpl.fooBar() at 2am every day -->

<property name="cronExpression" value="0 0 2 * * ?" />

<property name="jobDetail">

<bean class="frameworkx.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

<property name="concurrent" value="false"/>

<property name="targetClass" value="example.ExampleImpl" />

<property name="targetMethod" value="fooBar" />

</bean>

</property>

</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="triggers">

<list>

<ref bean="exampleTrigger" />

</list>

</property>

</bean>

*

* In this example we created a MethodInvokingJobDetailFactoryBean, which will produce a JobDetail Object with the jobClass property set to StatefulMethodInvokingJob.class (concurrent=="false"; Set to MethodInvokingJob.class when concurrent=="true"), which will in turn invoke the static fooBar() method of the "example.ExampleImpl" Class. The Scheduler is the heart of the whole operation; without it, nothing will happen.

*

* For more information on cronExpression syntax visit http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html

*

*@authorStephen M. Wick

*

*@see#afterPropertiesSet()*/

public class MethodInvokingJobDetailFactoryBean implementsFactoryBean, BeanNameAware, InitializingBean

{private Log logger =LogFactory.getLog(getClass());/*** The JobDetail produced by the afterPropertiesSet method of this Class will be assigned to the Group specified by this property. Default: Scheduler.DEFAULT_GROUP

*@see#afterPropertiesSet()

*@seeScheduler#DEFAULT_GROUP*/

private String group =Scheduler.DEFAULT_GROUP;/*** Indicates whether or not the Bean Method should be invoked by more than one Scheduler at the specified time (like when deployed to a cluster, and/or when there are multiple Spring ApplicationContexts in a single JVM - Tomcat 5.5 creates 2 or more instances of the DispatcherServlet (a pool), which in turn creates a separate Spring ApplicationContext for each instance of the servlet)

*

* Used by afterPropertiesSet to set the JobDetail.jobClass to MethodInvokingJob.class or StatefulMethodInvokingJob.class when true or false, respectively. Default: true

*@see#afterPropertiesSet()*/

private boolean concurrent = true;/**Used to set the JobDetail.durable property. Default: false

*

Durability - if a job is non-durable, it is automatically deleted from the scheduler once there are no longer any active triggers associated with it.

*@seehttp://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html

*@see#afterPropertiesSet()*/

private boolean durable = false;/*** Used by afterPropertiesSet to set the JobDetail.volatile property. Default: false

*

Volatility - if a job is volatile, it is not persisted between re-starts of the Quartz scheduler.

*

I set the default to false to be the same as the default for a Quartz Trigger. An exception is thrown

* when the Trigger is non-volatile and the Job is volatile. If you want volatility, then you must set this property, and the Trigger's volatility property, to true.

*@seehttp://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html

*@see#afterPropertiesSet()*/

private boolean volatility = false;/*** Used by afterPropertiesSet to set the JobDetail.requestsRecovery property. Default: false

*

RequestsRecovery - if a job "requests recovery", and it is executing during the time of a 'hard shutdown' of the scheduler (i.e. the process it is running within crashes, or the machine is shut off), then it is re-executed when the scheduler is started again. In this case, the JobExecutionContext.isRecovering() method will return true.

*@seehttp://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html

*@see#afterPropertiesSet()*/

private boolean shouldRecover = false;/*** A list of names of JobListeners to associate with the JobDetail object created by this FactoryBean.

*

*@see#afterPropertiesSet()

**/

privateString[] jobListenerNames;/**The name assigned to this bean in the Spring ApplicationContext.

* Used by afterPropertiesSet to set the JobDetail.name property.

*@seeafterPropertiesSet()

*@seeJobDetail#setName(String)

**/

privateString beanName;/*** The JobDetail produced by the afterPropertiesSet method, and returned by the getObject method of the Spring FactoryBean interface.

*@see#afterPropertiesSet()

*@see#getObject()

*@seeFactoryBean

**/

privateJobDetail jobDetail;/*** The name of the Class to invoke.

**/

privateString targetClass;/*** The Object to invoke.

*

* {@link#targetClass} or targetObject must be set, but not both.

*

* This object must be Serializable when {@link#concurrent} is set to false.*/

privateObject targetObject;/*** The instance method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.

*

* targetMethod or {@link#staticMethod} should be set, but not both.

**/

privateString targetMethod;/*** The static method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.

*

* {@link#targetMethod} or staticMethod should be set, but not both.*/

privateString staticMethod;/*** Method arguments provided to the {@link#targetMethod} or {@link#staticMethod} specified.

*

* All arguments must be Serializable when {@link#concurrent} is set to false.

*

* I strongly urge you not to provide arguments until Quartz 1.6.1 has been released if you are using a JDBCJobStore with

* Microsoft SQL Server. There is a bug in version 1.6.0 that prevents Quartz from Serializing the Objects in the JobDataMap

* to the database. The workaround is to set the property "org.opensymphony.quaryz.useProperties = true" in your quartz.properties file,

* which tells Quartz not to serialize Objects in the JobDataMap, but to instead expect all String compliant values.*/

privateObject[] arguments;/*** Get the targetClass property.

*@see#targetClass

*@returntargetClass*/

publicString getTargetClass()

{returntargetClass;

}/*** Set the targetClass property.

*@see#targetClass*/

public voidsetTargetClass(String targetClass)

{this.targetClass =targetClass;

}/*** Get the targetMethod property.

*@see#targetMethod

*@returntargetMethod*/

publicString getTargetMethod()

{returntargetMethod;

}/*** Set the targetMethod property.

*@see#targetMethod*/

public voidsetTargetMethod(String targetMethod)

{this.targetMethod =targetMethod;

}/***@returnjobDetail - The JobDetail that is created by the afterPropertiesSet method of this FactoryBean

*@see#jobDetail

*@see#afterPropertiesSet()

*@seeFactoryBean#getObject()*/

public Object getObject() throwsException

{returnjobDetail;

}/***@returnJobDetail.class

*@seeFactoryBean#getObjectType()*/

publicClass getObjectType()

{return JobDetail.class;

}/***@returntrue

*@seeFactoryBean#isSingleton()*/

public booleanisSingleton()

{return true;

}/*** Set the beanName property.

*@see#beanName

*@seeBeanNameAware#setBeanName(String)*/

public voidsetBeanName(String beanName)

{this.beanName =beanName;

}/*** Invoked by the Spring container after all properties have been set.

*

* Sets the jobDetail property to a new instance of JobDetail

*

*

jobDetail.name is set to beanName

*

jobDetail.group is set to group

*

jobDetail.jobClass is set to MethodInvokingJob.class or StatefulMethodInvokingJob.class depending on whether the concurrent property is set to true or false, respectively.

*

jobDetail.durability is set to durable

*

jobDetail.volatility is set to volatility

*

jobDetail.requestsRecovery is set to shouldRecover

*

jobDetail.jobDataMap["targetClass"] is set to targetClass

*

jobDetail.jobDataMap["targetMethod"] is set to targetMethod

*

Each JobListener name in jobListenerNames is added to the jobDetail object.

*

*

* Logging occurs at the DEBUG and INFO levels; 4 lines at the DEBUG level, and 1 line at the INFO level.

*

*

DEBUG: start

*

DEBUG: Creating JobDetail {beanName}

*

DEBUG: Registering JobListener names with JobDetail object {beanName}

*

INFO: Created JobDetail: {jobDetail}; targetClass: {targetClass}; targetMethod: {targetMethod};

*

DEBUG: end

*

*@seeorg.springframework.beans.factory.InitializingBean#afterPropertiesSet()

*@seeJobDetail

*@see#jobDetail

*@see#beanName

*@see#group

*@seeMethodInvokingJob

*@seeStatefulMethodInvokingJob

*@see#durable

*@see#volatility

*@see#shouldRecover

*@see#targetClass

*@see#targetMethod

*@see#jobListenerNames*/

public void afterPropertiesSet() throwsException

{try{

logger.debug("start");

logger.debug("Creating JobDetail "+beanName);

jobDetail= newJobDetail();

jobDetail.setName(beanName);

jobDetail.setGroup(group);

jobDetail.setJobClass(concurrent? MethodInvokingJob.class : StatefulMethodInvokingJob.class);

jobDetail.setDurability(durable);

jobDetail.setVolatility(volatility);

jobDetail.setRequestsRecovery(shouldRecover);if(targetClass!=null)

jobDetail.getJobDataMap().put("targetClass", targetClass);if(targetObject!=null)

jobDetail.getJobDataMap().put("targetObject", targetObject);if(targetMethod!=null)

jobDetail.getJobDataMap().put("targetMethod", targetMethod);if(staticMethod!=null)

jobDetail.getJobDataMap().put("staticMethod", staticMethod);if(arguments!=null)

jobDetail.getJobDataMap().put("arguments", arguments);

logger.debug("Registering JobListener names with JobDetail object "+beanName);if (this.jobListenerNames != null) {for (int i = 0; i < this.jobListenerNames.length; i++) {this.jobDetail.addJobListener(this.jobListenerNames[i]);

}

}

logger.info("Created JobDetail: "+jobDetail+"; targetClass: "+targetClass+"; targetObject: "+targetObject+"; targetMethod: "+targetMethod+"; staticMethod: "+staticMethod+"; arguments: "+arguments+";");

}finally{

logger.debug("end");

}

}/*** Setter for the concurrent property.

*

*@paramconcurrent

*@see#concurrent*/

public void setConcurrent(booleanconcurrent)

{this.concurrent =concurrent;

}/*** setter for the durable property.

*

*@paramdurable

*

*@see#durable*/

public void setDurable(booleandurable)

{this.durable =durable;

}/*** setter for the group property.

*

*@paramgroup

*

*@see#group*/

public voidsetGroup(String group)

{this.group =group;

}/*** setter for the {@link#jobListenerNames} property.

*

*@paramjobListenerNames

*@see#jobListenerNames*/

public voidsetJobListenerNames(String[] jobListenerNames)

{this.jobListenerNames =jobListenerNames;

}/*** setter for the {@link#shouldRecover} property.

*

*@paramshouldRecover

*@see#shouldRecover*/

public void setShouldRecover(booleanshouldRecover)

{this.shouldRecover =shouldRecover;

}/*** setter for the {@link#volatility} property.

*

*@paramvolatility

*@see#volatility*/

public void setVolatility(booleanvolatility)

{this.volatility =volatility;

}/*** This is a cluster safe Job designed to invoke a method on any bean defined within the same Spring

* ApplicationContext.

*

* The only entries this Job expects in the JobDataMap are "targetClass" and "targetMethod".

* - It uses the value of the targetClass entry to get the desired bean from the Spring ApplicationContext.

* - It uses the value of the targetMethod entry to determine which method of the Bean (identified by targetClass) to invoke.

*

* It uses the static ApplicationContext in the MethodInvokingJobDetailFactoryBean,

* which is ApplicationContextAware, to get the Bean with which to invoke the method.

*

* All Exceptions thrown from the execute method are caught and wrapped in a JobExecutionException.

*

*@seeMethodInvokingJobDetailFactoryBean#applicationContext

*@see#execute(JobExecutionContext)

*

*@authorStephen M. Wick*/

public static class MethodInvokingJob implementsJob

{protected Log logger =LogFactory.getLog(getClass());/*** When invoked by a Quartz scheduler, execute invokes a method on a Class or Object in the JobExecutionContext provided.

*

* Implementation

* The Class is identified by the "targetClass" entry in the JobDataMap of the JobExecutionContext provided. If targetClass is specified, then targetMethod must be a static method.

* The Object is identified by the 'targetObject" entry in the JobDataMap of the JobExecutionContext provided. If targetObject is provided, then targetClass will be overwritten. This Object must be Serializable when concurrent is set to false.

* The method is identified by the "targetMethod" entry in the JobDataMap of the JobExecutionContext provided.

* The "staticMethod" entry in the JobDataMap of the JobExecutionContext can be used to specify a Class and Method in one entry (ie: "example.ExampleClass.someStaticMethod")

* The method arguments (an array of Objects) are identified by the "arguments" entry in the JobDataMap of the JobExecutionContext. All arguments must be Serializable when concurrent is set to false.

*

* Logging is provided at the DEBUG and INFO levels; 8 lines at the DEBUG level, and 1 line at the INFO level.

*@seeJob#execute(JobExecutionContext)*/

public void execute(JobExecutionContext context) throwsJobExecutionException

{try{

logger.debug("start");

String targetClass= context.getMergedJobDataMap().getString("targetClass");

logger.debug("targetClass is "+targetClass);

Class targetClassClass= null;if(targetClass!=null)

{

targetClassClass= Class.forName(targetClass); //Could throw ClassNotFoundException

}

Object targetObject= context.getMergedJobDataMap().get("targetObject");

logger.debug("targetObject is "+targetObject);

String targetMethod= context.getMergedJobDataMap().getString("targetMethod");

logger.debug("targetMethod is "+targetMethod);

String staticMethod= context.getMergedJobDataMap().getString("staticMethod");

logger.debug("staticMethod is "+staticMethod);

Object[] arguments= (Object[])context.getMergedJobDataMap().get("arguments");

logger.debug("arguments are "+arguments);

logger.debug("creating MethodInvoker");

MethodInvoker methodInvoker= newMethodInvoker();

methodInvoker.setTargetClass(targetClassClass);

methodInvoker.setTargetObject(targetObject);

methodInvoker.setTargetMethod(targetMethod);

methodInvoker.setStaticMethod(staticMethod);

methodInvoker.setArguments(arguments);

methodInvoker.prepare();

logger.info("Invoking: "+methodInvoker.getPreparedMethod().toGenericString());

methodInvoker.invoke();

}catch(Exception e)

{throw newJobExecutionException(e);

}finally{

logger.debug("end");

}

}

}public static class StatefulMethodInvokingJob extends MethodInvokingJob implementsStatefulJob

{//No additional functionality; just needs to implement StatefulJob.

}publicObject[] getArguments()

{returnarguments;

}public voidsetArguments(Object[] arguments)

{this.arguments =arguments;

}publicString getStaticMethod()

{returnstaticMethod;

}public voidsetStaticMethod(String staticMethod)

{this.staticMethod =staticMethod;

}public voidsetTargetObject(Object targetObject)

{this.targetObject =targetObject;

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值