Spring 3 調度器示例 - JDK 定時器和 Quartz 展示
Spring框架提供了執行和調度任務的抽象,支持線程池或者在應用服務器環境中代理給CommonJ. Spring也集成了支持使用JDK Timer和Quartz調度庫提供的Quartz Scheduler來實現任務調度的類.兩種調度器通過分別引用可選的Timer或者org.quartz.Trigger實例的工廠Bean來進行設置. 另外,還有一個可以同時滿足Timer和Quartz Scheduler的類允許我們調用一個存在的目標對象的方法.
在這篇教程中,我們將向你展示在Spring中如何實現一個JDK Timer的例子,然后我們將使用Quartz Scheduler來豐富這個例子.
我們首選的開發環境是Eclipse. 我們使用的Eclipse版本是Eclipse Juno (4.2)同時集成了版本為 3.1.0的Maven插件. 你可以從這里下載Eclipse,然后從這里下載Maven插件.Eclipse中Maven插件的安裝不在本教程的范圍之內,我們將不在此進行討論. 我們還用到了Spring3.2.3和JDK 7_u_21.
讓我們開始吧.
1. 創建一個新的maven項目
Go to File -> Project ->Maven -> Maven Project.
在 “Select project name and location”向導頁, 選擇 “Create a simple project (skip archetype selection)”這個選項, 其他默認,點擊“Next”.
在 “Enter an artifact id” 這個頁面, 你可以定義你的項目名稱和主包. 我們將設置 “Group Id” 的值為"com.javacodegeeks.snippets.enterprise"以及 “Artifact Id” 的值為"springexample". 這亮相是項目的所在包"com.javacodegeeks.snippets.enterprise.springexample"和項目名稱"springexample".點擊 “Finish”退出向導,會自動簡歷項目.
Maven 項目結構:
It consists of the following folders:- /src/main/java 文件夾,包含程序源文件信息 ,
- /src/test/java 文件夾是所有測試信息contains all source files for unit tests,
- /src/main/resources文件夾放置所有配置文件
- /target 文件夾 放置編譯和打包好的文件
- pom.xml是項目對象模型(POM)文件. 這一個文件包含項目所有的相關配置信息 </ul>
- 在POM編輯器的 “Overview” 頁中,定位到“Properties”一節,進行下列變更:
創建一個新的名為org.springframework.version的屬性把其值設置為3.2.3.RELEASE. - 切換到“Dependencies”頁上,創建下面的依賴(你需要在該頁的“Dependency Details”部分中填寫“GroupId”, “Artifact Id”和“Version”等字段):
Group Id : org.springframework Artifact Id : spring-web Version : ${org.springframework.version}
</ul>
2. 添加Spring 3.2.3依賴
或者,你也可以直接在Maven的pom.xml文件中添加Spring依賴,即直接在POM編輯器的“Pom.xml”頁上進行編輯,如下所示:
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0 ; <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.snippets.enterprise</groupId> <artifactId>springexample</artifactId> <version>0.0.1-SNAPSHOT</version><dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies> <properties> <spring.version>3.2.3.RELEASE</spring.version> </properties>
</project></pre>
正如你所見,Maven通過聲明的方式來管理包依賴。一個本地庫會被創建(默認在{user_home}/.m2 目錄中),所有需要的類庫會從共有庫中下載下來放置到本地庫中。進而在其內部 – 庫依賴關系會被自動的處理和控制。
3.添加 Quartz 依賴
在項目的pom.xml文件中添加 Quartz 依賴,下面是添加后的pom.xml文件內容
<project xmlns="http://maven.apache.org/POM/4.0.0 ; <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.snippets.enterprise</groupId> <artifactId>springexample</artifactId> <version>0.0.1-SNAPSHOT</version><dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>3.1.2.RELEASE</version> </dependency> <!-- 這里是 Quartz 框架依賴--> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>1.8.6</version> </dependency> </dependencies> <properties> <spring.version>3.2.3.RELEASE</spring.version> </properties>
</project></pre>
4. 在Spring 中使用JDK 自帶的定時任務
4.1 創建一個簡單的任務
下面的MyTask.java 是個簡單的可以執行的任務MyTask.java
package com.javacodegeeks.snippets.enterprise;public class MyTask { public void sayHello() { System.out.println("Hello !!! "); } }</pre>
4.2 在applicationContext.xml中配置Scheduler和Timer
Spring 使用自己實現的Task取代了JDK中的TimerTask,Spring的Task是可以被定時調用或者通過Timer來重復調用的,它就是org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean。它也提供了對于JDK Timer的一個實現,它是一個后臺線程用來調度任務的工具類,這個類就是org.springframework.scheduling.timer.ScheduledTimerTask。
另外,還定義了一個TimerFactoryBean, 來啟動計劃任務的執行. 它是一個工廠Bean,它設置了一個暴露給其他bean引用的Timer. 它允許注冊ScheduledTimerTask,在容器啟動的時候開啟Timer然后在容器銷毀的時候將它取消。需要執行計劃任務的時候,我們需要做的就是在applicationContext.xml文件中定義上面所有提到過的類. 我們在myTask bean中指定MyTask.java類. 我們也需要定義schedulerBean, 它引用TimerTask類. 它有兩個屬性需要配置. targetObject的值引用我們自己實現的任務bean,也就是myTask. targetMethod屬性的值是需要被計劃執行的任務方法.
timerTask這個bean是個Timer類型的bean。它有一個timerTask屬性,我們可以通過它設置對上面定義好的名為timerTask的bean的引用。這里我們可以使用delay參數來配置第一次開始任務前的延時時間,單位是毫秒。我們也可以使用period參數來設置重復執行任務之間的間隔,單位也是毫秒。我們把它配置成每5秒鐘執行一次,首次延時1秒鐘。
TimerFactoryBean也定義在applicationContext.xml中。我們可以使用FactoryBean創建的Timer通過scheduledTimerTask參數,來注冊一系列ScheduledTimerTask對象。這里,我們注冊了定義好的timerTask。
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task ;<bean id="myTask" class="com.javacodegeeks.snippets.enterprise.MyTask" />
<bean id="schedulerTask" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean"> <property name="targetObject" ref="myTask" /> <property name="targetMethod" value="sayHello" /> </bean>
<bean id="timerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"> <property name="timerTask" ref="schedulerTask" /> <property name="delay" value="1000" /> <property name="period" value="5000" /> </bean>
<bean class="org.springframework.scheduling.timer.TimerFactoryBean"> <property name="scheduledTimerTasks"> <list> <ref local="timerTask" /> </list> </property> </bean> </beans></pre>
5 Quartz調度器
5.1 Quartz調度器任務
為了使用Quartz調度器來豐富我們的例子,首先我們必須設置一個Quartzs調度器任務。可以用兩種方式實現這一工作。第一種方式是在applicationContext.xml中把調度任務定義成Spring的一個bean。這個bean可以是一個org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean類,有兩個配置參數targetObject和targetMethod,就像我們在上一步中所做的一樣。不過,讓我們來嘗試一個更加復雜的調度器吧。我們僅需創建一個類來繼承org.springframework.scheduling.quartz.QuartzJobBean,就可以實現我們自己的兩個調度器。MyTask.java可以通過一個setter方法傳遞給調度器。隨后,任務里的方法會在繼承自QuartzJobBean的調度任務的executeInternal(JobExecutionContext context)方法中被調用。
QuartzJob.java
package com.javacodegeeks.snippets.enterprise.quartz;import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean;
import com.javacodegeeks.snippets.enterprise.MyTask;
public class QuartzJob extends QuartzJobBean {
private MyTask myTask; public void setMyTask(MyTask myTask) { this.myTask = myTask; } protected void executeInternal(JobExecutionContext context) throws JobExecutionException { myTask.sayHello(); }
}</pre>
5.2 Quartz觸發器
現在我們必須要來定義運行調度任務的Quartz觸發器了。Quartz提供了兩個類來實現觸發器部分。在org.springframework.scheduling.quartz.SimpleTriggerBean中我們可以設置startTime,endTime和intervals來運行任務,而在org.springframework.scheduling.quartz.CronTriggerBean則支持UNIX的cron表達式來指定任務運行的時間。兩種觸發器類型都被定義成Spring的bean,而且兩者都提供了一個jobDetail參數來設置對調度任務的引用。
還有一個配置工作是創建一個SchedulerFactoryBean,它可以整合調度任務和觸發器。所以兩個bean都被包含在SchedulerFactoryBean的定義中。
在applicationContext.xml中我們定義了所有的的bean,包括調度任務、觸發器和SchedulerFactoryBean。
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task ;<bean id="myTask" class="com.javacodegeeks.snippets.enterprise.MyTask" />
<!-- quartz --> <bean name="quartzJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="com.javacodegeeks.snippets.enterprise.quartz.QuartzJob" /> <property name="jobDataAsMap"> <map> <entry key="myTask" value-ref="myTask" /> </map> </property> </bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="quartzJob" /> <property name="repeatInterval" value="5000" /> <property name="startDelay" value="1000" /> </bean> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="quartzJob" /> <property name="cronExpression" value="0/5 * * * * ?" /> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="jobDetails"> <list> <ref bean="quartzJob" /> </list> </property> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> </bean>
</beans></pre>
注意,我們添加的兩個觸發器的實現,在SchedulerFactoryBean中只使用了其中一個。cronTrigger是org.springframework.scheduling.quartz.CronTriggerBean類型的實現,而simpleTrigger是org.springframework.scheduling.quartz.SimpleTriggerBean類型的實現。在SchedulerFactoryBean類型的bean的triggers參數中設置了對cronTrigger的引用,而在jobDetails參數中設置了對調度任務的引用。
需要注意的另外一件事是,名為quartzJob的bean的定義。這里通過一個參數把Quartz任務類設定為org.springframework.scheduling.quartz.JobDetailBean類。該類有一個jobDataAsMap參數,任務會在這里進行注冊。
6. 運行應用
在App.java類里,我們裝載了applicationContext.xml文件并使運行應用程序的線程休眠30秒后再關閉上下文。一旦上下文被打開,任務就會像上面安排的那樣開始運行。sayHello()方法每5秒鐘被調用一次,兩個案例都是。
App.java
package com.javacodegeeks.snippets.enterprise;import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) throws InterruptedException { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Thread.sleep(30000); context.close(); }
}</pre>
這是關于Spring 3 Scheduler的兩個實現JDK Timer和Quartz的一個例子。
可以下載該教程的Eclipse項目 : SpringSchedulerQuartzExample.zip