1. 程式人生 > >Spring整合Quartz框架實現定時任務跑批(Maven完整版)

Spring整合Quartz框架實現定時任務跑批(Maven完整版)

觸發器 delay cut www 方法 lin job 定時任務 任務調度

Quartz 介紹
Quartz is a full-featured, open source job scheduling service that can be integrated with, or

used along side virtually any Java application - from the smallest stand-alone application to

the largest e-commerce system. Quartz can be used to create simple or complex schedules for

executing tens, hundreds, or even tens-of-thousands of jobs;

Quartz框架是一個全功能、開源的任務調度服務,可以集成幾乎任何的java應用程序—從小的單片機系統到大型
的電子商務系統。Quartz可以執行上千上萬的任務調度。
Quartz核心的概念:scheduler任務調度、Job任務、Trigger觸發器、JobDetail任務細節
Spring框架提供了對Quartz框架的支持,這對我們來說,方便了很多。我們還是通過Maven工程來演示。1,pom.xml配置文件內容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
    <modelVersion>4.0.0</modelVersion>  

    <groupId>com.yangcq</groupId>  
    <artifactId>SpringQuartzTest</artifactId>  
    <version>1.0.0</version>  
    <packaging>jar</packaging>  
    <name>SpringQuartzTest</name>  

    <properties>  
        <springframework.version>4.0.6.RELEASE</springframework.version><!-- Spring的版本 --> 
        <quartz.version>2.2.1</quartz.version>                          <!-- Quartz的版本 --> 
    </properties>  

    <dependencies>  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-core</artifactId>  
            <version>${springframework.version}</version>  
        </dependency>  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-context-support</artifactId>  
            <version>${springframework.version}</version>  
        </dependency>  
        <!-- Spring事務的依賴 -->  
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-tx</artifactId>  
            <version>${springframework.version}</version>  
        </dependency>  

        <!-- 定時任務框架Quartz的依賴-->  
        <dependency>  
            <groupId>org.quartz-scheduler</groupId>  
            <artifactId>quartz</artifactId>  
            <version>${quartz.version}</version>  
        </dependency>  

    </dependencies>  
</project>  

2,定義我們自己的Job實現類。
Spring框架整合Quartz時,不是直接繼承Job類,而是繼承QuartzJobBean,
我們這裏的一個實現如下:

package com.yangcq.quartz;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
 * 
 * @author yangcq
 * @description 由於Spring提供對Quartz的支持,所以直接使用Spring提供的API
 * @description 繼承  org.springframework.scheduling.quartz.QuartzJobBean
 *
 */
public class EBankJob extends QuartzJobBean {  
  /**
   * 
   */
  private EBankJobBean eBankJobBean; 

  @Override
    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {  
    eBankJobBean.printAnotherMessage();  
    }  

    public void setEBankJobBean(EBankJobBean eBankJobBean) {  
        this.eBankJobBean = eBankJobBean;  
    }  

} 

這部分代碼就是一個典型的spring註入,實際上調用了eBankJobBean的方法。具體job的執行是在executeInternal
方法裏。
問題一:目前這種方法沒有測試通過,分析一下什麽原因?
問題二:MethodInvokingJobDetailFactoryBean 與 JobDetailBean 的區別,研究Spring源碼
答:
1,QuartzJobBean是Spring框架下的一個抽象類,這個類實現了Quartz的Job接口。我們可以理解為,Spring對Job的
進一步封裝,
public abstract class QuartzJobBean implements Job
2,JobDetailBean也是繼承了Quartz下面的JobDetail接口,這裏為什麽是繼承,本人也感到費解,按說JobDetail是
一個抽象接口,
應該用implements關鍵字實現這個接口,具體代碼如下:
public class JobDetailBean extends JobDetail
3,MethodInvokingJobDetailFactoryBean,這個類為我們設置定時任務,提供了豐富的支持,如下:

  private String name;
  private String group;
  private boolean concurrent;
  private String targetBeanName;
  private String[] jobListenerNames;
  private String beanName;
  private ClassLoader beanClassLoader;
  private BeanFactory beanFactory;
  private JobDetail jobDetail;
package com.yangcq.quartz;
/**
 * 
 * @author yangcq
 *
 */
public class EBankJobBean {
    public void printAnotherMessage(){  
        System.out.println("CronTriggerBean 調用的定時任務...");  
    }   
}

因為是要將這些任務通過spring的配置文件來拼接到一起,我們來看看具體的配置文件該怎麽設置。
在spring裏,如果我們要執行一個計劃任務,需要定義一個JobDetail,用它來封裝我們具體執行的任務。結合前面純quartz的示例,
我們發現它們其實本質上是一樣的。這裏的定義如下:
spring默認提供了一個叫MethodInvokingJobDetailFactoryBean,我們需要將定義好的對象和需要調用的方法傳給它。
這裏對應的是一種類型的jobDetail定義。對應的myBean定義如下:

package com.yangcq.quartz;
/**
 * 
 * @author yangcq
 * @description SimpleTriggerFactoryBean 調用的定時任務
 */
public class MyJobBean {
    public void printMessage() {  
        System.out.println("SimpleTriggerFactoryBean 調用的定時任務...");  
    }    
}

OK,下面寫一個測試類,進行測試

package com.yangcq.quartz;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 
 * @author yangcq
 * @description 啟動定時任務main方法
 *
 */
public class StartUpQuartzJobs {

  static final Log log = LogFactory.getLog(StartUpQuartzJobs.class); // 日誌

  public static void main(String args[]) throws Exception {
    log.info("開始啟動定時任務 ...");
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
    log.info("定時任務啟動成功...");
  }
}

接下來就是關鍵部分了,Spring核心配置文件的的配置,配置文件applicationContext.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>  
<!-- Bean頭部 -->  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:util="http://www.springframework.org/schema/util"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">  

    <!-- 啟動包掃描功能,以便註冊帶有@Controller、@Service、@repository、@Component等註解的類成為spring的bean -->   
    <context:component-scan base-package="com.yangcq.quartz" /> 

    <bean id="myJobBean" class="com.yangcq.quartz.MyJobBean"></bean>
    <bean id="eBankJobBean" class="com.yangcq.quartz.EBankJobBean"></bean>

    <bean id="simpleJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject" ref="myJobBean" />  
        <property name="targetMethod" value="printMessage" />  
    </bean>  
    <!-- 這種封裝的方式很簡單,就是我定義了一個對象和它對應的方法。如果我們需要將它們封裝成一個job了,只要把類的名字和 -->
    <!-- 對應的方法傳進去就可以了。除了上述的JobDetail構造方式,還要一種更復雜一些,它的定義如下: -->
    <!--   這裏因為要用到一些對象的引用,對這些對象或者參數的傳遞可以通過jobDataMap來處理。 -->
    <bean name="complexJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject" ref="eBankJobBean" />  
        <property name="targetMethod" value="printAnotherMessage" /> 
    </bean> 

    <!-- Spring提供了2中觸發器:SimpleTriggerBean 和 CronTriggerBean -->

    <!-- Trigger觸發器 :從第1秒開始,每3秒執行一次 -->
    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">  
        <property name="jobDetail" ref="simpleJobDetail" />  
        <property name="startDelay" value="1000" />
        <property name="repeatInterval" value="3000" />  
    </bean>  
    <!-- Trigger觸發器 :從第1秒開始,每5秒執行一次 -->
    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  
        <property name="jobDetail" ref="complexJobDetail" />  
        <property name="cronExpression" value="0/5 * * * * ?" />  
    </bean>  

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="jobDetails">  
            <list>  
                <ref bean="simpleJobDetail" />  
                <ref bean="complexJobDetail" />  
            </list>  
        </property>  

        <property name="triggers">  
            <list>  
                <ref bean="simpleTrigger" />  
                <ref bean="cronTrigger" />  
            </list>  
        </property>  
    </bean>    
</beans> 
啟動程序以後,控制臺輸出如下:
log4j:WARN No appenders could be found for logger (com.yangcq.quartz.StartUpQuartzJobs).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
SimpleTriggerFactoryBean 調用的定時任務...
CronTriggerBean 調用的定時任務...
SimpleTriggerFactoryBean 調用的定時任務...
CronTriggerBean 調用的定時任務...
SimpleTriggerFactoryBean 調用的定時任務...

總結
在這兩個示例裏,我們首先通過一個純手工的過程來完成一個任務調度的示例。它的主要步驟為
1.定義job

  1. 定義trigger
  2. 定義scheduler來拼接。
    在後續使用spring的示例裏,其實也是這麽一個步驟,只不過spring提供了一些實現的支持,需要在配置文件裏指定
    不同的jobDetail類型和trigger類型。

Spring整合Quartz框架實現定時任務跑批(Maven完整版)