`

MethodInvokingJobDetailFactoryBean

阅读更多

利用Quartz来实现对任务的调度已经被广泛地应用了,一个利用Quartz来进行任务调度的典型配置如下:

<bean id="testTask" class="com.alisoft.xx.TestTask" />   
<bean id="xxJobDetail"    
    class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">   
    <property name="concurrent">   
        <value>false</value>   
    </property>   
    <property name="targetObject">   
        <ref bean="testTask"/>   
    </property>   
    <property name="targetMethod">   
        <value>execute</value>   
    </property>   
</bean>   
<!-- SimpleTriggerBean可以用org.springframework.scheduling.quartz.CronTriggerBean代替 -->   
<bean id="xxTriggerBean" class="org.springframework.scheduling.quartz.SimpleTriggerBean">   
    <property name="jobDetail">   
        <ref bean="xxJobDetail"/>   
    </property>   
    <property name="startDelay">   
        <value>1000</value>   
    </property>   
    <property name="repeatInterval">   
        <value>60000</value>   
    </property>   
</bean>   
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">   
    <property name="triggers">   
        <list>   
            <ref local="xxTriggerBean"/>   
        </list>   
    </property>   
</bean>  
<bean id="testTask" class="com.alisoft.xx.TestTask" />
<bean id="xxJobDetail" 
 class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
 <property name="concurrent">
  <value>false</value>
 </property>
    <property name="targetObject">
        <ref bean="testTask"/>
    </property>
    <property name="targetMethod">
        <value>execute</value>
    </property>
</bean>
<!-- SimpleTriggerBean可以用org.springframework.scheduling.quartz.CronTriggerBean代替 -->
<bean id="xxTriggerBean" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
    <property name="jobDetail">
        <ref bean="xxJobDetail"/>
    </property>
    <property name="startDelay">
        <value>1000</value>
    </property>
    <property name="repeatInterval">
        <value>60000</value>
    </property>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
   <ref local="xxTriggerBean"/>
        </list>
    </property>
</bean> 

 

若有多个任务需要调度,则配置多个JobDetail、Trigger即可,待执行的Task bean没有啥任何要求,不需要extends任何class、或者implements任何interface,只是被执行的method需要是无参数的;

但是在我们现实项目中,仅仅完成这样的功能是还不够的,我们还需要争取完成以下几点:

对所有的Task实现执行开始、结束时间的记录;
对每天的Task执行结果持久化、或者通过email、旺旺消息等方式告知相关owner;
当我们有多台Task服务器时,如何保证每个任务只在一台服务器上执行;
针对以上几个问题,我们也都有很简单、直接的办法可以搞定,比如说1、2两点可以直接在每个任务中各自编码完成,但是这样不仅每个task都要写非常类似的重复代码、而且不能保证每个任务的执行情况都被记录,因为某些task的编码人员就不会在意这些非功能性需求;对于第3点,我们也可以通过配置来完成向不同task服务器部署不一样的发布包来完成,但这给发布带来了麻烦,这意味着有多少台task服务器,就需要通过修改配置重新打包并发布多少次;

其实我们可以利用Spring默认提供的AOP来非常优雅的解决这几个问题:扩展MethodInvokingJobDetailFactoryBean来实现对任务调度时的拦截!其关键代码为MethodInvokingJobDetailFactoryBean.MethodInvokingJob.executeInternal(JobExecutionContext context);这个method中;

具体步骤如下:

新增所有Task都需要实现的interface,可参考如下代码:

public interface TaskHandler {   
    /**  
     * 任务调度执行需要实现的method  
     */  
    TaskResult execute();   
}  
public interface TaskHandler {
 /**
  * 任务调度执行需要实现的method
  */
 TaskResult execute();
} 

 
基于MethodInvokingJobDetailFactoryBean实现自定义的JobDetailFactoryBean,在具体执行待调度任务的method前后加入公用逻辑,比如记录开始、结束日期、判断该task是否由该台服务器执行、任务执行完成之后将运行结果进行持久化或者发email等给相关owner;可参考如下代码:

public class AppsMethodInvokingJobDetailFactoryBean extends  
        MethodInvokingJobDetailFactoryBean {   
    protected static final Log logger = LogFactory.getLog(AppsMethodInvokingJobDetailFactoryBean.class);   
       
    /* (non-Javadoc)  
     * @see org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean#afterPropertiesSet()  
     */  
    @Override  
    public void afterPropertiesSet() throws ClassNotFoundException,   
            NoSuchMethodException {   
        super.afterPropertiesSet();   
           
        logger.info("origin jobClass : " + ((JobDetail)super.getObject()).getJobClass().getName());   
        // Consider the concurrent flag to choose between stateful and stateless job.   
        if(MethodInvokingJob.class.getName().equals(((JobDetail)super.getObject()).getJobClass().getName())) {   
            ((JobDetail)super.getObject()).setJobClass(AppsMethodInvokingJob.class);   
        } else {   
            ((JobDetail)super.getObject()).setJobClass(AppsStatefulMethodInvokingJob.class);   
        }   
        logger.info("new jobClass : " + ((JobDetail)super.getObject()).getJobClass().getName());   
    }   
  
    public static class AppsMethodInvokingJob extends MethodInvokingJob {   
        protected static final Log logger = LogFactory.getLog(AppsMethodInvokingJob.class);   
        private MethodInvoker methodInvoker;   
        private String errorMessage;   
  
        /**  
         * Set the MethodInvoker to use.  
         */  
        public void setMethodInvoker(MethodInvoker methodInvoker) {   
            this.methodInvoker = methodInvoker;   
            this.errorMessage = "Could not invoke method '" + this.methodInvoker.getTargetMethod() +   
                    "' on target object [" + this.methodInvoker.getTargetObject() + "]";   
        }   
  
        /**  
         * Invoke the method via the MethodInvoker.  
         */  
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {   
            Date startDate = new Date();   
            String taskName = methodInvoker.getTargetClass().getName();   
            TaskResult taskResult;   
            try {   
                if (logger.isInfoEnabled()) {   
                    logger.info(taskName + " job start at " + startDate);   
                }   
                   
                //根据当前服务器主机名或者IP判断是否需要执行该任务   
                //TODO Code   
                   
                //调用具体task执行method代码   
                taskResult = this.methodInvoker.invoke();                  
            } catch (Exception ex) {   
                logger.error(taskName + " accounted a error : " + this.errorMessage, ex);   
                throw new JobExecutionException(this.errorMessage, ex, false);   
            } finally {   
                if(logger.isInfoEnabled()) {   
                    logger.info(taskName + " job end   at " + new Date());   
                }   
                   
                //将task执行结果taskResult进行持久化、或者通知相关owner   
                //TODO Code   
            }   
        }   
    }   
  
    public static class AppsStatefulMethodInvokingJob extends AppsMethodInvokingJob {   
    }   
}  
public class AppsMethodInvokingJobDetailFactoryBean extends
  MethodInvokingJobDetailFactoryBean {
 protected static final Log logger = LogFactory.getLog(AppsMethodInvokingJobDetailFactoryBean.class);
 
 /* (non-Javadoc)
  * @see org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean#afterPropertiesSet()
  */
 @Override
 public void afterPropertiesSet() throws ClassNotFoundException,
   NoSuchMethodException {
  super.afterPropertiesSet();
  
  logger.info("origin jobClass : " + ((JobDetail)super.getObject()).getJobClass().getName());
  // Consider the concurrent flag to choose between stateful and stateless job.
  if(MethodInvokingJob.class.getName().equals(((JobDetail)super.getObject()).getJobClass().getName())) {
   ((JobDetail)super.getObject()).setJobClass(AppsMethodInvokingJob.class);
  } else {
   ((JobDetail)super.getObject()).setJobClass(AppsStatefulMethodInvokingJob.class);
  }
  logger.info("new jobClass : " + ((JobDetail)super.getObject()).getJobClass().getName());
 }

 public static class AppsMethodInvokingJob extends MethodInvokingJob {
  protected static final Log logger = LogFactory.getLog(AppsMethodInvokingJob.class);
  private MethodInvoker methodInvoker;
  private String errorMessage;

  /**
   * Set the MethodInvoker to use.
   */
  public void setMethodInvoker(MethodInvoker methodInvoker) {
   this.methodInvoker = methodInvoker;
   this.errorMessage = "Could not invoke method '" + this.methodInvoker.getTargetMethod() +
     "' on target object [" + this.methodInvoker.getTargetObject() + "]";
  }

  /**
   * Invoke the method via the MethodInvoker.
   */
  protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
   Date startDate = new Date();
   String taskName = methodInvoker.getTargetClass().getName();
   TaskResult taskResult;
   try {
    if (logger.isInfoEnabled()) {
     logger.info(taskName + " job start at " + startDate);
    }
    
    //根据当前服务器主机名或者IP判断是否需要执行该任务
    //TODO Code
    
    //调用具体task执行method代码
    taskResult = this.methodInvoker.invoke();    
   } catch (Exception ex) {
    logger.error(taskName + " accounted a error : " + this.errorMessage, ex);
    throw new JobExecutionException(this.errorMessage, ex, false);
   } finally {
    if(logger.isInfoEnabled()) {
     logger.info(taskName + " job end   at " + new Date());
    }
    
    //将task执行结果taskResult进行持久化、或者通知相关owner
    //TODO Code
   }
  }
 }

 public static class AppsStatefulMethodInvokingJob extends AppsMethodInvokingJob {
 }
} 

 

 

将自定义JobDetailFactoryBean的bean配置设置为abstract,从而减少每个task的相关配置量,新的代码可参考如下配置:

<bean id="appsAbstractJobDetail" class="com.alisoft...AppsMethodInvokingJobDetailFactoryBean"  
    abstract="true">   
    <property name="concurrent" value="false" />   
    <property name="targetMethod" value="execute" />   
</bean>   
<bean id="testTaskHandler" class="com.alisoft...task.TestTaskHandler" />   
<bean id="testTaskJobDetail" parent="appsAbstractJobDetail">         
    <property name="targetObject" ref="testTaskHandler" />   
</bean>   
<bean id="testTaskTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">   
    <property name="jobDetail" ref="testTaskJobDetail" />   
    <property name="cronExpression" value="0 10 0 * * ?" />   
</bean>  

 

分享到:
评论

相关推荐

    MethodInvokingJobDetailFactoryBean.java

    class="frameworkx.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"&gt; &lt;property name="shouldRecover" value="false"&gt;&lt;/property&gt; class="org.springframework.scheduling....

    spring 定时任务配置详解

    MethodInvokingJobDetailFactoryBean 可以让开发者轻松地配置 JobDetail 对象,并通过 targetObject 和 targetMethod 属性指定要调用的方法。 Spring 提供了多种方式来配置定时任务,可以根据实际情况选择合适的...

    Spring 整合Quartz两种方式

    本文将深入探讨如何在Spring环境中整合Quartz,主要关注两种方式:JobDetailBean和MethodInvokingJobDetailFactoryBean。这两种方式都能帮助我们在Spring应用中实现定时任务,但它们的使用方法和适用场景略有不同。 ...

    spring quartz 集群模式

    例如,`MethodInvokingJobDetailFactoryBean`是用于创建一个基于方法调用的Job实例,它允许你直接指定一个目标对象和执行的方法,这样每次任务执行时就会调用该方法。 集群模式在Quartz中称为“集群(Clustering)...

    spring设置动态定时任务.pdf

    通过正确配置`MethodInvokingJobDetailFactoryBean`、`CronTriggerBean`以及`SchedulerFactoryBean`,并结合业务接口动态更新Cron表达式,我们可以灵活地控制任务的执行。同时,确保并发控制和任务管理的安全性是...

    spring+quartz定时集群支持

    总结起来,"Spring+Quartz定时集群支持"涉及到的知识点主要包括:Spring与Quartz的集成、Quartz的集群配置、`MethodInvokingJobDetailFactoryBean`的使用、数据库表的设计以及通过IHS等手段实现的任务调度。...

    spring结合quartz的定时的2种方式

    2. **配置 MethodInvokingJobDetailFactoryBean**:接下来,在 Spring 配置文件中使用 `&lt;bean&gt;` 标签定义一个 `MethodInvokingJobDetailFactoryBean` 类型的 bean,并设置相应的属性。这些属性包括: - `target...

    spring 定时器时间配置.doc

    在Spring中整合Quartz,我们可以利用Spring的`SchedulerFactoryBean`来创建和管理调度器,同时使用`MethodInvokingJobDetailFactoryBean`和`CronTriggerBean`来定义要执行的任务和触发执行的时间。 首先,我们需要...

    spring定时器

    MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); jobDetail.setTargetObject(myTaskService); jobDetail.setTargetMethod("executeTask"); return jobDetail....

    quarzt定时调度任务解析

    在Spring配置中,我们可以使用`MethodInvokingJobDetailFactoryBean`来创建JobDetail,并设置目标对象和方法。 3. **触发器 (Trigger)**:触发器定义了任务何时被执行的规则。比如`CronTrigger`允许我们使用cron...

    spring-quartz.zip

    Spring 提供了 `org.springframework.scheduling.quartz` 包,包含了与 Quartz 集成所需的各种组件,如 `SchedulerFactoryBean`、`MethodInvokingJobDetailFactoryBean` 和 `CronTriggerFactoryBean`。 例如,我们...

    Spring 时间配置

    在Spring中使用Quartz,首先需要配置`MethodInvokingJobDetailFactoryBean`,它定义了定时任务的具体行为,即哪个对象的哪个方法需要被调用。例如: ```xml ...

    Spring任务调度配置及说明(spring+quartz)

    这部分定义了一个任务详情bean,名为"jobtask",它使用`MethodInvokingJobDetailFactoryBean`来调用`quartzJob`bean的`work`方法。这意味着当定时任务触发时,`Task`类的`work`方法会被执行。 接下来是触发器的配置...

    ssm集成Quartz1

    【SSM集成Quartz1】是指在Spring、Struts2和...接着,使用`MethodInvokingJobDetailFactoryBean`来指定任务执行的具体方法,如`doTask()`,并设置是否允许并发执行。 对于`jobPasswordExpired`,配置如下: ```xml ...

    spring设置动态定时任务.doc

    &lt;bean id="schedulerJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"&gt; ``` 这里`simpleJobTest()`是待执行的方法,`concurrent`属性设置为`false`表示...

    spring整合quartz常见问题

    - **适用于MethodInvokingJobDetailFactoryBean**: 由于该类型通常需要存储非字符串类型的复杂对象,因此应将其设置为`false`,同时需要处理好对象的序列化和反序列化过程。 **扩展解释**: - 当使用`...

    spring 定时器

    根据提供的部分代码片段,我们可以看到Spring配置文件中定义了一个名为`safe`的任务对象,并通过Quartz的`MethodInvokingJobDetailFactoryBean`创建了一个JobDetail。接下来定义了一个CronTrigger,设置了cron表达式...

    spring2.5 定时器任务

    2. **`MethodInvokingJobDetailFactoryBean`**:该类用于创建Quartz JobDetail实例,它定义了要执行的方法及其所在对象。 3. **`CronTriggerBean`**与**`SimpleTriggerBean`**:这两个类分别用于基于Cron表达式和...

    java定时任务制定

    Spring通过`JobDetailBean`和`MethodInvokingJobDetailFactoryBean`等类进一步简化了Quartz的使用。 `JobDetailBean`是Spring提供的一个用于配置Quartz Job的类。在XML配置中,你可以指定`jobClass`,这是你的...

Global site tag (gtag.js) - Google Analytics