解决Spring中使用quartz发生NotSerializableException methodInvoker的问题

本文主要是介绍解决Spring中使用quartz发生NotSerializableException methodInvoker的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Spring2.5 + quartz1.6.5 发生如下异常

 

Caused by: org.quartz.JobPersistenceException: Couldn't store job: Unable to serialize JobDataMap for insertion into database because the value of property 'methodInvoker' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean [See nested exception: java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property 'methodInvoker' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean]
        at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1105)

 

这是Spring的Bug, 解决方法 http://jira.springframework.org/browse/SPR-3797
下载BeanInvokingJobDetailFactoryBean.java 和 MethodInvokingJobDetailFactoryBean.java
在Spring配置文件中使用如下方式

 

Xml代码   收藏代码
  1. <bean id="exampleBean" class="example.ExampleImpl">   
  2. </bean>  
  3.   
  4. <bean id="exampleTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">   
  5.     <!-- Execute exampleBean.fooBar() at 2am every day -->  
  6.     <property name="cronExpression" value="0 0 2 * * ?" />  
  7.     <property name="jobDetail">   
  8.         <bean class="frameworkx.springframework.scheduling.quartz.BeanInvokingJobDetailFactoryBean">   
  9.             <property name="concurrent" value="false"/>  
  10.             <property name="targetBean" value="exampleBean" />  
  11.             <property name="targetMethod" value="fooBar" />  
  12.             <property name="arguments">   
  13.                 <list>   
  14.                     <value>arg1Value</value>  
  15.                     <value>arg2Value</value>   
  16.                 <list>   
  17.             </property>   
  18.         </bean>   
  19.     </property>   
  20. </bean>   
  21.   
  22. <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">   
  23.     <property name="triggers">   
  24.         <list>   
  25.             <ref bean="exampleTrigger" />   
  26.         </list>   
  27.     </property>   
  28. </bean>  
 

将BeanInvokingJobDetailFactoryBean.java 和 MethodInvokingJobDetailFactoryBean.java

需要考到项目中。。。用法和上面一样

 

参考 http://soulshard.iteye.com/blog/337886

BeanInvokingJobDetailFactoryBean.java 和MethodInvokingJobDetailFactoryBean.java源码地址:

https://jira.spring.io/secure/attachment/12821/BeanInvokingJobDetailFactoryBean.java

https://jira.spring.io/secure/attachment/12820/MethodInvokingJobDetailFactoryBean.java


下面是我拷贝过来的

BeanInvokingJobDetailFactoryBean.java 源码:


package frameworkx.springframework.scheduling.quartz;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.MethodInvoker;


/**
 * This is a cluster safe Quartz/Spring FactoryBean implementation, which produces a JobDetail implementation that can invoke any no-arg method on any bean deployed within a Spring container.
 * <p>
 * Use this Class instead of the MethodInvokingJobDetailBeanFactory Class provided by Spring when deploying to a web environment like Tomcat.
 * <p>
 * <b>Implementation</b><br>
 * The Spring ApplicationContext cannot be passed to a Job via the JobDataMap, because it is not Serializable (and for very good reason!)
 * So, instead of associating an ApplicationContext with a JobDetail or a Trigger object, I made the [Stateful]BeanInvokingJob, which is not persisted in the database, get the applicationContext from the BeanInvokingJobDetailFactoryBean, which is ApplicationContextAware, when the [Stateful]BeanInvokingJob is created and executed.
 * <p>
 * The name or id of the of the bean to invoke (targetBean) and the method to invoke (targetMethod) must be provided in the bean declaration or a JobExecutionException will be thrown.
 * <p>
 * I wrote BeanInvokingJobDetailFactoryBean, because the MethodInvokingJobDetailFactoryBean does not produce Serializable
 * JobDetail objects, and as a result cannot be deployed into a clustered environment (as is documented within the Class).
 * <p>
 * <b>Example</b>
 * <code>
 * <ul>
 * &lt;bean id="<i>exampleBean</i>" class="example.ExampleImpl"&gt;
&lt;/bean&gt;
<p>
 * &lt;bean id="<i>exampleTrigger</i>" class="org.springframework.scheduling.quartz.CronTriggerBean"&gt;
 * <ul>
<i>&lt;!-- Execute exampleBean.fooBar() at 2am every day --&gt;</i><br>
&lt;property name="cronExpression" value="0 0 2 * * ?" /&gt;<br>
&lt;property name="jobDetail"&gt;
<ul>
&lt;bean class="frameworkx.springframework.scheduling.quartz.<b>BeanInvokingJobDetailFactoryBean</b>"&gt;
<ul>
&lt;property name="concurrent" value="<i>false</i>"/&gt;<br>
&lt;property name="targetBean" value="<i>exampleBean</i>" /&gt;<br>
&lt;property name="targetMethod" value="<i>fooBar</i>" /&gt;<br>
&lt;property name="arguments"&gt;
<ul>
&lt;list&gt;
<ul>
&lt;value&gt;arg1Value&lt;/value&gt;<br>
&lt;value&gt;arg2Value&lt;/value&gt;
</ul>
&lt;list&gt;
</ul>
&lt;/property&gt;
</ul>
&lt;/bean&gt;
</ul>
&lt;/property&gt;
</ul>
&lt;/bean&gt;
<p>
&lt;bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"&gt;
<ul>
&lt;property name="triggers"&gt;
<ul>
&lt;list&gt;
<ul>
&lt;ref bean="<i>exampleTrigger</i>" /&gt;
</ul>
&lt;/list&gt;
</ul>
&lt;/property&gt;
</ul>
&lt;/bean&gt;
</ul>
 * </code>
 * In this example we created a BeanInvokingJobDetailFactoryBean, which will produce a JobDetail Object with the jobClass property set to StatefulBeanInvokingJob.class (concurrent=="false"; Set to BeanInvokingJob.class when concurrent=="true"), which will in turn invoke the <code>fooBar</code>(String, String) method of the bean with id "<code>exampleBean</code>".  Method <code>arguments</code> are optional.  In this case there are two String arguments being provided to the <code>fooBar</code> method.  The Scheduler is the heart of the whole operation; without it, nothing will happen.
 * <p>
  For more information on cronExpression visit <a href="http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html">http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html</a>
 * 
 * <p>
 * <b>Troubleshooting</b>
 * <p>
 * <b>Error:</b> java.io.IOException: JobDataMap values must be Strings when the 'useProperties' property is set.  Key of offending value: arguments<br>
 * <b>Solution:</b> do not set the <code>arguments</code> property when <code>org.quartz.jobstore.useProperty</code> is set to "true" in <code>quartz.properties</code>.
 * <p>
 * @author Stephen M. Wick
 *
 * @see #afterPropertiesSet()
 */
public class BeanInvokingJobDetailFactoryBean implements FactoryBean, BeanNameAware, InitializingBean, ApplicationContextAware
{
/**
* Set by <code>setApplicationContext</code> when a BeanInvokingJobDetailFactoryBean is defined within a Spring ApplicationContext as a bean.
* <p>
* Used by the <code>execute</code> method of the BeanInvokingJob and StatefulBeanInvokingJob classes.
* @see #setApplicationContext(ApplicationContext)
* @see BeanInvokingJob#execute(JobExecutionContext)
*/
protected static ApplicationContext applicationContext;


private Log logger = LogFactory.getLog(getClass());


/**
* The JobDetail produced by the <code>afterPropertiesSet</code> method of this Class will be assigned to the Group specified by this property.  Default: Scheduler.DEFAULT_GROUP 
* @see #afterPropertiesSet()
* @see Scheduler#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<i> - 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</i>) 
* <p>
* Used by <code>afterPropertiesSet</code> to set the JobDetail.jobClass to BeanInvokingJob.class or StatefulBeanInvokingJob.class when true or false, respectively.  Default: true 
* @see #afterPropertiesSet()
*/
private boolean concurrent = true;

/** Used to set the JobDetail.durable property.  Default: false
* <p>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.
* @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a> 
* @see #afterPropertiesSet() 
*/
private boolean durable = false;

/**
* Used by <code>afterPropertiesSet</code> to set the JobDetail.volatile property.  Default: false
* <p>Volatility - if a job is volatile, it is not persisted between re-starts of the Quartz scheduler.
* <p>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.
* @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
* @see #afterPropertiesSet() 
*/
private boolean volatility = false;

/** 
* Used by <code>afterPropertiesSet</code> to set the JobDetail.requestsRecovery property.  Default: false<BR>
* <p>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. 
* @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a> 
* @see #afterPropertiesSet() 
*/
private boolean shouldRecover = false;

/**
* A list of names of JobListeners to associate with the JobDetail object created by this FactoryBean.
*
* @see #afterPropertiesSet() 
**/
private String[] jobListenerNames;

/** The name assigned to this bean in the Spring ApplicationContext.
* Used by <code>afterPropertiesSet</code> to set the JobDetail.name property.
* @see afterPropertiesSet()
* @see JobDetail#setName(String)
**/
private String beanName;

/**
* The JobDetail produced by the <code>afterPropertiesSet</code> method, and returned by the <code>getObject</code> method of the Spring FactoryBean interface.
* @see #afterPropertiesSet()
* @see #getObject()
* @see FactoryBean
**/
private JobDetail jobDetail;

/** 
* The name or id of the bean to invoke, as it is declared in the Spring ApplicationContext.
**/
private String targetBean;

/**
* The method to invoke on the bean identified by the targetBean property. 
**/
private String targetMethod;

/**
* The arguments to provide to the method identified by the targetMethod property.
* These Objects must be Serializable when concurrent=="true".
*/
private Object[] arguments;

/**
* Get the targetBean property.
* @see #targetBean
* @return targetBean
*/
public String getTargetBean()
{
return targetBean;
}


/**
* Set the targetBean property.
* @see #targetBean
*/
public void setTargetBean(String targetBean)
{
this.targetBean = targetBean;
}


/**
* Get the targetMethod property.
* @see #targetMethod
* @return targetMethod
*/
public String getTargetMethod()
{
return targetMethod;
}


/**
* Set the targetMethod property.
* @see #targetMethod
*/
public void setTargetMethod(String targetMethod)
{
this.targetMethod = targetMethod;
}


/**
* @return jobDetail - The JobDetail that is created by the afterPropertiesSet method of this FactoryBean
* @see #jobDetail
* @see #afterPropertiesSet()
* @see FactoryBean#getObject()
*/
public Object getObject() throws Exception
{
return jobDetail;
}


/**
* @return JobDetail.class
* @see FactoryBean#getObjectType()
*/
public Class getObjectType()
{
return JobDetail.class;
}


/**
* @return true
* @see FactoryBean#isSingleton()
*/
public boolean isSingleton()
{
return true;
}


/**
* Set the beanName property.
* @see #beanName
* @see BeanNameAware#setBeanName(String)
*/
public void setBeanName(String beanName)
{
this.beanName = beanName;
}


/**
* Invoked by the Spring container after all properties have been set.
* <p>
* Sets the <code>jobDetail</code> property to a new instance of JobDetail
* <ul>
* <li>jobDetail.name is set to <code>beanName</code><br>
* <li>jobDetail.group is set to <code>group</code><br>
* <li>jobDetail.jobClass is set to BeanInvokingJob.class or StatefulBeanInvokingJob.class depending on whether the <code>concurrent</code> property is set to true or false, respectively.<br>
* <li>jobDetail.durability is set to <code>durable</code>
* <li>jobDetail.volatility is set to <code>volatility</code>
* <li>jobDetail.requestsRecovery is set to <code>shouldRecover</code>
* <li>jobDetail.jobDataMap["targetBean"] is set to <code>targetBean</code>
* <li>jobDetail.jobDataMap["targetMethod"] is set to <code>targetMethod</code>
* <li>jobDetail.jobDataMap["arguments"] is set to <code>arguments</code>
* <li>Each JobListener name in <code>jobListenerNames</code> is added to the <code>jobDetail</code> object.
* </ul>
* <p>
* Logging occurs at the DEBUG and INFO levels; 4 lines at the DEBUG level, and 1 line at the INFO level.
* <ul>
* <li>DEBUG: start
* <li>DEBUG: Creating JobDetail <code>{beanName}</code>
* <li>DEBUG: Registering JobListener names with JobDetail object <code>{beanName}</code>
* <li>INFO: Created JobDetail: <code>{jobDetail}</code>; targetBean: <code>{targetBean}</code>; targetMethod: <code>{targetMethod}</code>; arguments: <code>{arguments}</code>;
* <li>DEBUG: end
* </ul>
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
* @see JobDetail
* @see #jobDetail
* @see #beanName
* @see #group
* @see BeanInvokingJob
* @see StatefulBeanInvokingJob
* @see #durable
* @see #volatility
* @see #shouldRecover
* @see #targetBean
* @see #targetMethod
* @see #arguments
* @see #jobListenerNames 
*/
public void afterPropertiesSet() throws Exception
{
try
{
logger.debug("start");

logger.debug("Creating JobDetail "+beanName);
jobDetail = new JobDetail();
jobDetail.setName(beanName);
jobDetail.setGroup(group);
jobDetail.setJobClass(concurrent ? BeanInvokingJob.class : StatefulBeanInvokingJob.class);
jobDetail.setDurability(durable);
jobDetail.setVolatility(volatility);
jobDetail.setRequestsRecovery(shouldRecover);
jobDetail.getJobDataMap().put("targetBean", targetBean);
jobDetail.getJobDataMap().put("targetMethod", targetMethod);
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+"; targetBean: "+targetBean+"; targetMethod: "+targetMethod+"; arguments: "+arguments+";");
}
finally
{
logger.debug("end");
}
}


/**
* Setter for the concurrent property.

* @param concurrent
* @see #concurrent
*/
public void setConcurrent(boolean concurrent)
{
this.concurrent = concurrent;
}


/**
* setter for the durable property.

* @param durable

* @see #durable
*/
public void setDurable(boolean durable)
{
this.durable = durable;
}


/**
* setter for the group property.

* @param group

* @see #group
*/
public void setGroup(String group)
{
this.group = group;
}


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

* @param jobListenerNames
* @see #jobListenerNames
*/
public void setJobListenerNames(String[] jobListenerNames)
{
this.jobListenerNames = jobListenerNames;
}


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

* @param shouldRecover
* @see #shouldRecover
*/
public void setShouldRecover(boolean shouldRecover)
{
this.shouldRecover = shouldRecover;
}


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

* @param volatility
* @see #volatility
*/
public void setVolatility(boolean volatility)
{
this.volatility = volatility;
}


/**
* Set the Spring ApplicationContext in which this class has been deployed.
* <p>
* Invoked by Spring as a result of the ApplicationContextAware interface implemented by this Class.

* @see ApplicationContextAware#setApplicationContext(ApplicationContext)
*/
public void setApplicationContext(ApplicationContext context) throws BeansException
{
applicationContext = context;
}


public void setArguments(Object[] arguments)
{
this.arguments = arguments;
}


/**
* This is a cluster safe Job designed to invoke a method on any bean defined within the same Spring
* ApplicationContext.
* <p>
* The only entries this Job expects in the JobDataMap are "targetBean" and "targetMethod".<br>
* - It uses the value of the <code>targetBean</code> entry to get the desired bean from the Spring ApplicationContext.<br>
* - It uses the value of the <code>targetMethod</code> entry to determine which method of the Bean (identified by targetBean) to invoke.
* <p>
* It uses the static ApplicationContext in the BeanInvokingJobDetailFactoryBean,
* which is ApplicationContextAware, to get the Bean with which to invoke the method.
* <p>
* All Exceptions thrown from the execute method are caught and wrapped in a JobExecutionException.

* @see BeanInvokingJobDetailFactoryBean#applicationContext
* @see #execute(JobExecutionContext)

* @author Stephen M. Wick
*/
public static class BeanInvokingJob implements Job
{
protected Log logger = LogFactory.getLog(getClass());

/**
* When invoked by a Quartz scheduler, <code>execute</code> invokes a method on a bean deployed within the scheduler's Spring ApplicationContext.
* <p>
* <b>Implementation</b><br>
* The bean is identified by the "targetBean" entry in the JobDataMap of the JobExecutionContext provided.<br>
* The method is identified by the "targetMethod" entry in the JobDataMap of the JobExecutionContext provided.<br>
* <p>
* The Quartz scheduler shouldn't start up correctly if the bean identified by "targetBean" cannot be found in the scheduler's Spring ApplicationContext.  BeanFactory.getBean()
* throws an exception if the targetBean doesn't exist, so I'm not going to waste any code testing for the bean's existance in the ApplicationContext. 
* <p>
* Logging is provided at the DEBUG and INFO levels; 5 lines at the DEBUG level, and 1 line at the INFO level.
* @see Job#execute(JobExecutionContext)
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
try
{
logger.debug("start");

String targetBean = context.getMergedJobDataMap().getString("targetBean");
logger.debug("targetBean is "+targetBean);
if(targetBean==null)
throw new JobExecutionException("targetBean cannot be null.", false);

String targetMethod = context.getMergedJobDataMap().getString("targetMethod");
logger.debug("targetMethod is "+targetMethod);
if(targetMethod==null)
throw new JobExecutionException("targetMethod cannot be null.", false);

// when org.quartz.jobStore.useProperties=="true" the arguments entry (which should be an Object[]) in the JobDataMap gets converted into a String.
Object argumentsObject = context.getMergedJobDataMap().get("arguments");
Object[] arguments = (argumentsObject instanceof String) ? null : (Object[])argumentsObject;
logger.debug("arguments array is "+arguments);

Object bean = applicationContext.getBean(targetBean);
logger.debug("applicationContext resolved bean name/id '"+targetBean+"' to "+bean);

MethodInvoker beanMethod = new MethodInvoker();
beanMethod.setTargetObject(bean);
beanMethod.setTargetMethod(targetMethod);
beanMethod.setArguments(arguments);
beanMethod.prepare();
logger.info("Invoking Bean: "+targetBean+"; Method: "+targetMethod+"; arguments: "+arguments+";");
beanMethod.invoke();
}
catch(JobExecutionException e)
{
throw e;
}
catch(Exception e)
{
throw new JobExecutionException(e);
}
finally
{
logger.debug("end");
}
}
}


public static class StatefulBeanInvokingJob extends BeanInvokingJob implements StatefulJob
{
// No additional functionality; just needs to implement StatefulJob.
}
}




MethodInvokingJobDetailFactoryBean.java源码:

package frameworkx.springframework.scheduling.quartz;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.MethodInvoker;


/**
 * This is a cluster safe Quartz/Spring FactoryBean implementation, which produces a JobDetail implementation that can invoke any no-arg method on any Class.
 * <p>
 * Use this Class instead of the MethodInvokingJobDetailBeanFactory Class provided by Spring when deploying to a web environment like Tomcat.
 * <p>
 * <b>Implementation</b><br>
 * 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.
 * <p>
 * A method can be invoked one of several ways:
 * <ul>
 * <li>The name of the Class to invoke (targetClass) and the static method to invoke (targetMethod) can be specified.
 * <li>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").
 * <li>The Class and static Method to invoke can be specified in one property (staticMethod). example: staticMethod = "example.ExampleClass.someStaticMethod"
 * <br><b>Note:</b>  An Object[] of method arguments can be specified (arguments), but the Objects must be Serializable if concurrent=="false".
 * </ul>  
 * <p>
 * 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).
 * <p>
 * <b>Example</b>
 * <code>
 * <ul>
 * &lt;bean id="<i>exampleTrigger</i>" class="org.springframework.scheduling.quartz.CronTriggerBean"&gt;
 * <ul>
<i>&lt;!-- Execute example.ExampleImpl.fooBar() at 2am every day --&gt;</i><br>
&lt;property name="<a href="http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html">cronExpression</a>" value="0 0 2 * * ?" /&gt;<br>
&lt;property name="jobDetail"&gt;
<ul>
&lt;bean class="frameworkx.springframework.scheduling.quartz.<b>MethodInvokingJobDetailFactoryBean</b>"&gt;
<ul>
&lt;property name="concurrent" value="<i>false</i>"/&gt;<br>
&lt;property name="targetClass" value="<i>example.ExampleImpl</i>" /&gt;<br>
&lt;property name="targetMethod" value="<i>fooBar</i>" /&gt;
</ul>
&lt;/bean&gt;
</ul>
&lt;/property&gt;
</ul>
&lt;/bean&gt;
<p>
&lt;bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"&gt;
<ul>
&lt;property name="triggers"&gt;
<ul>
&lt;list&gt;
<ul>
&lt;ref bean="<i>exampleTrigger</i>" /&gt;
</ul>
&lt;/list&gt;
</ul>
&lt;/property&gt;
</ul>
&lt;/bean&gt;
</ul>
 * </code>
 * 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 <code>fooBar</code>() method of the "<code>example.ExampleImpl</code>" Class. The Scheduler is the heart of the whole operation; without it, nothing will happen.
 * <p>
 * For more information on <code>cronExpression</code> syntax visit <a href="http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html">http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html</a>
 * 
 * @author Stephen M. Wick
 *
 * @see #afterPropertiesSet()
 */
public class MethodInvokingJobDetailFactoryBean implements FactoryBean, BeanNameAware, InitializingBean
{
private Log logger = LogFactory.getLog(getClass());


/**
* The JobDetail produced by the <code>afterPropertiesSet</code> method of this Class will be assigned to the Group specified by this property.  Default: Scheduler.DEFAULT_GROUP 
* @see #afterPropertiesSet()
* @see Scheduler#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<i> - 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</i>) 
* <p>
* Used by <code>afterPropertiesSet</code> 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
* <p>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.
* @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a> 
* @see #afterPropertiesSet() 
*/
private boolean durable = false;

/**
* Used by <code>afterPropertiesSet</code> to set the JobDetail.volatile property.  Default: false
* <p>Volatility - if a job is volatile, it is not persisted between re-starts of the Quartz scheduler.
* <p>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.
* @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
* @see #afterPropertiesSet() 
*/
private boolean volatility = false;

/** 
* Used by <code>afterPropertiesSet</code> to set the JobDetail.requestsRecovery property.  Default: false<BR>
* <p>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. 
* @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a> 
* @see #afterPropertiesSet() 
*/
private boolean shouldRecover = false;

/**
* A list of names of JobListeners to associate with the JobDetail object created by this FactoryBean.
*
* @see #afterPropertiesSet() 
**/
private String[] jobListenerNames;

/** The name assigned to this bean in the Spring ApplicationContext.
* Used by <code>afterPropertiesSet</code> to set the JobDetail.name property.
* @see afterPropertiesSet()
* @see JobDetail#setName(String)
**/
private String beanName;

/**
* The JobDetail produced by the <code>afterPropertiesSet</code> method, and returned by the <code>getObject</code> method of the Spring FactoryBean interface.
* @see #afterPropertiesSet()
* @see #getObject()
* @see FactoryBean
**/
private JobDetail jobDetail;

/**
* The name of the Class to invoke.
**/
private String targetClass;


/**
* The Object to invoke.
* <p>
* {@link #targetClass} or targetObject must be set, but not both.
* <p>
* This object must be Serializable when {@link #concurrent} is set to false.
*/
private Object targetObject;

/**
* The instance method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
* <p>
* targetMethod or {@link #staticMethod} should be set, but not both. 
**/
private String targetMethod;

/**
* The static method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
* <p>
* {@link #targetMethod} or staticMethod should be set, but not both. 
*/
private String staticMethod;


/**
* Method arguments provided to the {@link #targetMethod} or {@link #staticMethod} specified.
* <p>
* All arguments must be Serializable when {@link #concurrent} is set to false.
* <p>
* 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.
*/
private Object[] arguments;


/**
* Get the targetClass property.
* @see #targetClass
* @return targetClass
*/
public String getTargetClass()
{
return targetClass;
}


/**
* Set the targetClass property.
* @see #targetClass
*/
public void setTargetClass(String targetClass)
{
this.targetClass = targetClass;
}


/**
* Get the targetMethod property.
* @see #targetMethod
* @return targetMethod
*/
public String getTargetMethod()
{
return targetMethod;
}


/**
* Set the targetMethod property.
* @see #targetMethod
*/
public void setTargetMethod(String targetMethod)
{
this.targetMethod = targetMethod;
}


/**
* @return jobDetail - The JobDetail that is created by the afterPropertiesSet method of this FactoryBean
* @see #jobDetail
* @see #afterPropertiesSet()
* @see FactoryBean#getObject()
*/
public Object getObject() throws Exception
{
return jobDetail;
}


/**
* @return JobDetail.class
* @see FactoryBean#getObjectType()
*/
public Class getObjectType()
{
return JobDetail.class;
}


/**
* @return true
* @see FactoryBean#isSingleton()
*/
public boolean isSingleton()
{
return true;
}


/**
* Set the beanName property.
* @see #beanName
* @see BeanNameAware#setBeanName(String)
*/
public void setBeanName(String beanName)
{
this.beanName = beanName;
}


/**
* Invoked by the Spring container after all properties have been set.
* <p>
* Sets the <code>jobDetail</code> property to a new instance of JobDetail
* <ul>
* <li>jobDetail.name is set to <code>beanName</code><br>
* <li>jobDetail.group is set to <code>group</code><br>
* <li>jobDetail.jobClass is set to MethodInvokingJob.class or StatefulMethodInvokingJob.class depending on whether the <code>concurrent</code> property is set to true or false, respectively.<br>
* <li>jobDetail.durability is set to <code>durable</code>
* <li>jobDetail.volatility is set to <code>volatility</code>
* <li>jobDetail.requestsRecovery is set to <code>shouldRecover</code>
* <li>jobDetail.jobDataMap["targetClass"] is set to <code>targetClass</code>
* <li>jobDetail.jobDataMap["targetMethod"] is set to <code>targetMethod</code>
* <li>Each JobListener name in <code>jobListenerNames</code> is added to the <code>jobDetail</code> object.
* </ul>
* <p>
* Logging occurs at the DEBUG and INFO levels; 4 lines at the DEBUG level, and 1 line at the INFO level.
* <ul>
* <li>DEBUG: start
* <li>DEBUG: Creating JobDetail <code>{beanName}</code>
* <li>DEBUG: Registering JobListener names with JobDetail object <code>{beanName}</code>
* <li>INFO: Created JobDetail: <code>{jobDetail}</code>; targetClass: <code>{targetClass}</code>; targetMethod: <code>{targetMethod}</code>;
* <li>DEBUG: end
* </ul>
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
* @see JobDetail
* @see #jobDetail
* @see #beanName
* @see #group
* @see MethodInvokingJob
* @see StatefulMethodInvokingJob
* @see #durable
* @see #volatility
* @see #shouldRecover
* @see #targetClass
* @see #targetMethod
* @see #jobListenerNames 
*/
public void afterPropertiesSet() throws Exception
{
try
{
logger.debug("start");

logger.debug("Creating JobDetail "+beanName);
jobDetail = new JobDetail();
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.

* @param concurrent
* @see #concurrent
*/
public void setConcurrent(boolean concurrent)
{
this.concurrent = concurrent;
}


/**
* setter for the durable property.

* @param durable

* @see #durable
*/
public void setDurable(boolean durable)
{
this.durable = durable;
}


/**
* setter for the group property.

* @param group

* @see #group
*/
public void setGroup(String group)
{
this.group = group;
}


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

* @param jobListenerNames
* @see #jobListenerNames
*/
public void setJobListenerNames(String[] jobListenerNames)
{
this.jobListenerNames = jobListenerNames;
}


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

* @param shouldRecover
* @see #shouldRecover
*/
public void setShouldRecover(boolean shouldRecover)
{
this.shouldRecover = shouldRecover;
}


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

* @param volatility
* @see #volatility
*/
public void setVolatility(boolean volatility)
{
this.volatility = volatility;
}


/**
* This is a cluster safe Job designed to invoke a method on any bean defined within the same Spring
* ApplicationContext.
* <p>
* The only entries this Job expects in the JobDataMap are "targetClass" and "targetMethod".<br>
* - It uses the value of the <code>targetClass</code> entry to get the desired bean from the Spring ApplicationContext.<br>
* - It uses the value of the <code>targetMethod</code> entry to determine which method of the Bean (identified by targetClass) to invoke.
* <p>
* It uses the static ApplicationContext in the MethodInvokingJobDetailFactoryBean,
* which is ApplicationContextAware, to get the Bean with which to invoke the method.
* <p>
* All Exceptions thrown from the execute method are caught and wrapped in a JobExecutionException.

* @see MethodInvokingJobDetailFactoryBean#applicationContext
* @see #execute(JobExecutionContext)

* @author Stephen M. Wick
*/
public static class MethodInvokingJob implements Job
{
protected Log logger = LogFactory.getLog(getClass());

/**
* When invoked by a Quartz scheduler, <code>execute</code> invokes a method on a Class or Object in the JobExecutionContext provided.
* <p>
* <b>Implementation</b><br>
* 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.<br>
* 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 <code>concurrent</code> is set to false.<br>
* The method is identified by the "targetMethod" entry in the JobDataMap of the JobExecutionContext provided.<br>
* 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")<br>
* 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 <code>concurrent</code> is set to false.
* <p>
* Logging is provided at the DEBUG and INFO levels; 8 lines at the DEBUG level, and 1 line at the INFO level.
* @see Job#execute(JobExecutionContext)
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
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 = new MethodInvoker();
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 new JobExecutionException(e);
}
finally
{
logger.debug("end");
}
}
}


public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob
{
// No additional functionality; just needs to implement StatefulJob.
}


public Object[] getArguments()
{
return arguments;
}


public void setArguments(Object[] arguments)
{
this.arguments = arguments;
}


public String getStaticMethod()
{
return staticMethod;
}


public void setStaticMethod(String staticMethod)
{
this.staticMethod = staticMethod;
}


public void setTargetObject(Object targetObject)
{
this.targetObject = targetObject;
}
}



除此之外我在实际开发中还遇到了一个问题

spring quartz 的 数据库持久化出现的问题

数据源:的defaultAutoCommit 的要设置成false     



Spring Quartz 集成基于数据库持久化出现错误的解决

出现如下错误:

[html]  view plain copy
  1. Caused by: org.quartz.JobPersistenceException: Couldn’t store trigger: The job (DEFAULT.driverJob)  
  2.  referenced by the trigger does not exist.   
  3. [See nested exception: org.quartz.JobPersistenceException: The job (DEFAULT.driverJob) referenced by the trigger does not exist.]  


解决方法:

原因是数据源中加入了
< property name =”defaultAutoCommit” value =”false” />
去掉或者改成true就行了

[html]  view plain copy
  1. <!-- quartz使用的数据源配置 -->  
  2. <bean id="quartzDataSource"  class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  3.     <property name="driverClassName" value="${quartz.jdbc.driver}" />  
  4.     <property name="url" value="${quartz.jdbc.url}" />  
  5.     <property name="username" value="${quartz.jdbc.username}" />  
  6.     <property name="password" value="${quartz.jdbc.password}" />  
  7.     <property name="defaultAutoCommit" value="true" />  
  8. </bean>  




这篇关于解决Spring中使用quartz发生NotSerializableException methodInvoker的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1093292

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数