Quartz中扩展MethodInvokingJobDetailFactoryBean实现...

来源:百度文库 编辑:神马文学网 时间:2024/04/28 04:45:18

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

view plaincopy to clipboardprint?

  1.   
  2.     class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">   
  3.        
  4.         false   
  5.        
  6.        
  7.            
  8.        
  9.        
  10.         execute   
  11.        
  12.   
  13.   
  14.   
  15.        
  16.            
  17.        
  18.        
  19.         1000   
  20.        
  21.        
  22.         60000   
  23.        
  24.   
  25.   
  26.        
  27.            
  28.                
  29.            
  30.        
  31.   

 

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

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

  1. 对所有的Task实现执行开始、结束时间的记录;
  2. 对每天的Task执行结果持久化、或者通过email、旺旺消息等方式告知相关owner;
  3. 当我们有多台Task服务器时,如何保证每个任务只在一台服务器上执行;

针对以上几个问题,我们也都有很简单、直接的办法可以搞定,比如说1、2两点可以直接在每个任务中各自编码完成,但是这样不仅每个task都要写非常类似的重复代码、而且不能保证每个任务的执行情况都被记录,因为某些task的编码人员就不会在意这些非功能性需求;对于第3点,我们也可以通过配置来完成向不同task服务器部署不一样的发布包来完成,但这给发布带来了麻烦,这意味着有多少台task服务器,就需要通过修改配置重新打包并发布多少次;

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

具体步骤如下:

  1. 新增所有Task都需要实现的interface,可参考如下代码:
    view plaincopy to clipboardprint?
    1. public interface TaskHandler {   
    2.     /**  
    3.      * 任务调度执行需要实现的method  
    4.      */  
    5.     TaskResult execute();   
    6. }  
  2. 基于MethodInvokingJobDetailFactoryBean实现自定义的JobDetailFactoryBean,在具体执行待调度任务的method前后加入公用逻辑,比如记录开始、结束日期、判断该task是否由该台服务器执行、任务执行完成之后将运行结果进行持久化或者发email等给相关owner;可参考如下代码:
    view plaincopy to clipboardprint?
    1. public class AppsMethodInvokingJobDetailFactoryBean extends  
    2.         MethodInvokingJobDetailFactoryBean {   
    3.     protected static final Log logger = LogFactory.getLog(AppsMethodInvokingJobDetailFactoryBean.class);   
    4.        
    5.     /* (non-Javadoc)  
    6.      * @see org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean#afterPropertiesSet()  
    7.      */  
    8.     @Override  
    9.     public void afterPropertiesSet() throws ClassNotFoundException,   
    10.             NoSuchMethodException {   
    11.         super.afterPropertiesSet();   
    12.            
    13.         logger.info("origin jobClass : " + ((JobDetail)super.getObject()).getJobClass().getName());   
    14.         // Consider the concurrent flag to choose between stateful and stateless job.   
    15.         if(MethodInvokingJob.class.getName().equals(((JobDetail)super.getObject()).getJobClass().getName())) {   
    16.             ((JobDetail)super.getObject()).setJobClass(AppsMethodInvokingJob.class);   
    17.         } else {   
    18.             ((JobDetail)super.getObject()).setJobClass(AppsStatefulMethodInvokingJob.class);   
    19.         }   
    20.         logger.info("new jobClass : " + ((JobDetail)super.getObject()).getJobClass().getName());   
    21.     }   
    22.   
    23.     public static class AppsMethodInvokingJob extends MethodInvokingJob {   
    24.         protected static final Log logger = LogFactory.getLog(AppsMethodInvokingJob.class);   
    25.         private MethodInvoker methodInvoker;   
    26.         private String errorMessage;   
    27.   
    28.         /**  
    29.          * Set the MethodInvoker to use.  
    30.          */  
    31.         public void setMethodInvoker(MethodInvoker methodInvoker) {   
    32.             this.methodInvoker = methodInvoker;   
    33.             this.errorMessage = "Could not invoke method '" + this.methodInvoker.getTargetMethod() +   
    34.                     "' on target object [" + this.methodInvoker.getTargetObject() + "]";   
    35.         }   
    36.   
    37.         /**  
    38.          * Invoke the method via the MethodInvoker.  
    39.          */  
    40.         protected void executeInternal(JobExecutionContext context) throws JobExecutionException {   
    41.             Date startDate = new Date();   
    42.             String taskName = methodInvoker.getTargetClass().getName();   
    43.             TaskResult taskResult;   
    44.             try {   
    45.                 if (logger.isInfoEnabled()) {   
    46.                     logger.info(taskName + " job start at " + startDate);   
    47.                 }   
    48.                    
    49.                 //根据当前服务器主机名或者IP判断是否需要执行该任务   
    50.                 //TODO Code   
    51.                    
    52.                 //调用具体task执行method代码   
    53.                 taskResult = this.methodInvoker.invoke();                  
    54.             } catch (Exception ex) {   
    55.                 logger.error(taskName + " accounted a error : " + this.errorMessage, ex);   
    56.                 throw new JobExecutionException(this.errorMessage, ex, false);   
    57.             } finally {   
    58.                 if(logger.isInfoEnabled()) {   
    59.                     logger.info(taskName + " job end   at " + new Date());   
    60.                 }   
    61.                    
    62.                 //将task执行结果taskResult进行持久化、或者通知相关owner   
    63.                 //TODO Code   
    64.             }   
    65.         }   
    66.     }   
    67.   
    68.     public static class AppsStatefulMethodInvokingJob extends AppsMethodInvokingJob {   
    69.     }   
    70. }  
  3. 将自定义JobDetailFactoryBean的bean配置设置为abstract,从而减少每个task的相关配置量,新的代码可参考如下配置:
    view plaincopy to clipboardprint?
    1.     abstract="true">   
    2.        
    3.        
    4.   
    5.   
    6.          
    7.        
    8.   
    9.   
    10.        
    11.        
    12.