領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多繼 Keith 和 Chris 開啟的 Spring 3.0 “簡化系列”之後,我想簡要概述一下 Spring 3.0 在排程和任務執行方面所帶來的簡化。
我將透過一個簡單的 示例應用程式 進行講解,您可以從 spring-samples Subversion 倉庫檢出。該應用程式的設計力求簡潔,同時展示了 Spring 3.0 中基於註解和基於 XML 的任務排程方法。
讓我們從基於註解的方法開始。你可以直接透過 AnnotationDemo 中的 main() 方法來執行它。如果你仔細檢視,你會發現它不過是一個 Spring ApplicationContext 的載入程式。
public static void main(String[] args) {
new ClassPathXmlApplicationContext("config.xml", AnnotationDemo.class);
}
之所以不需要其他東西,是因為 ApplicationContext 包含了一個“活動”元件,我們稍後就會看到。正是因為這個元件,main() 方法才不會退出。config.xml 也同樣精簡,只包含兩個元素。
<context:component-scan base-package="org/springframework/samples/task/basic/annotation"/>
<task:annotation-driven/>
“component-scan”元素指向包含我們“beans”的包。有兩個:ScheduledProcessor 和 AsyncWorker。我們稍後會看它們,但首先來看看“annotation-driven”元素。這是 Spring 3.0 中新引入的,它驅動了兩個註解:@Scheduled 和 @Async。你可以分別使用“scheduler”和“executor”屬性來引用 Spring TaskScheduler 和 TaskExecutor,但對於這個示例,我們將只使用預設值。
ScheduledProcessor 在一個方法上包含 @Scheduled 註解,因此它是上面提到的“活動”元件。由於配置中存在“annotation-driven”元素,此方法將被註冊到 Spring TaskScheduler 例項,該例項將以 30 秒的固定延遲週期性地執行該方法。
@Service
public class ScheduledProcessor implements Processor {
private final AtomicInteger counter = new AtomicInteger();
@Autowired
private Worker worker;
@Scheduled(fixedDelay = 30000)
public void process() {
System.out.println("processing next 10 at " + new Date());
for (int i = 0; i < 10; i++) {
worker.work(counter.incrementAndGet());
}
}
}
正如你在上一個程式碼片段中看到的,Worker 在迴圈中被 ScheduledProcessor 呼叫。然而,AsyncWorker 實現包含對其 work(..) 方法的 @Async 註解,並且由於配置中的“annotation-driven”元素,它將被包裝在一個代理中,以便該方法實際上由 TaskExecutor 例項呼叫。為了驗證這一點,當前執行緒名稱會在該方法中顯示。同樣,為了說明工作是併發進行的,會呼叫 sleep(..) 來模擬耗時的操作。
@Component
public class AsyncWorker implements Worker {
@Async
public void work(int i) {
String threadName = Thread.currentThread().getName();
System.out.println(" " + threadName + " beginning work on " + i);
try {
Thread.sleep(5000); // simulates work
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(" " + threadName + " completed work on " + i);
}
}
如果你執行 AnnotationDemo 的 main() 方法,輸出應該類似於這樣: processing next 10 at Mon Jan 04 18:20:52 EST 2010 SimpleAsyncTaskExecutor-1 beginning work on 1 SimpleAsyncTaskExecutor-2 beginning work on 2 SimpleAsyncTaskExecutor-3 beginning work on 3 SimpleAsyncTaskExecutor-5 beginning work on 5 SimpleAsyncTaskExecutor-4 beginning work on 4 SimpleAsyncTaskExecutor-6 beginning work on 6 SimpleAsyncTaskExecutor-7 beginning work on 7 SimpleAsyncTaskExecutor-8 beginning work on 8 SimpleAsyncTaskExecutor-9 beginning work on 9 SimpleAsyncTaskExecutor-10 beginning work on 10 SimpleAsyncTaskExecutor-1 completed work on 1 SimpleAsyncTaskExecutor-2 completed work on 2 SimpleAsyncTaskExecutor-3 completed work on 3 SimpleAsyncTaskExecutor-5 completed work on 5 SimpleAsyncTaskExecutor-6 completed work on 6 SimpleAsyncTaskExecutor-7 completed work on 7 SimpleAsyncTaskExecutor-8 completed work on 8 SimpleAsyncTaskExecutor-4 completed work on 4 SimpleAsyncTaskExecutor-10 completed work on 10 SimpleAsyncTaskExecutor-9 completed work on 9
關於該輸出有幾點需要注意。首先,一次處理 10 行將每 30 秒重複一次(由於 @Scheduled)。其次,工作項由不同的執行緒併發處理(由於 @Async)。在最後一個“beginning work”訊息和第一個“completed work”訊息之間應該有大約 5 秒的延遲。如果所有工作程式都在單個執行緒中執行,我們將看到順序的 beginning/completed 對,整個過程將花費大約 50 秒。當然,時間方面無法在此博文中捕獲,因此你真的應該 下載 並自行執行示例以獲得完整體驗(該專案可以直接匯入到 SpringSource Tool Suite 或其他支援 Maven 的 Eclipse 類環境中)。
我想展示的最後一件事是 @Scheduled 註解的基於 XML 的替代方案。該示例包含另一個類 SimpleProcessor,它在其 process() 方法上沒有 @Scheduled 註解。
@Service
public class SimpleProcessor implements Processor {
private final AtomicInteger counter = new AtomicInteger();
public void process() {
System.out.println("processing next 10 at " + new Date());
for (int i = 0; i < 10; i++) {
System.out.println(" processing " + counter.incrementAndGet());
}
}
}
XML 比基於註解的方法只稍微冗長一些,因為 Spring 3.0 現在提供了一個“task”名稱空間來保持配置的簡潔。
<context:component-scan base-package="org/springframework/samples/task/basic/xml"/>
<task:scheduled-tasks>
<task:scheduled ref="simpleProcessor" method="process" cron="3/10 * * * * ?"/>
</task:scheduled-tasks>
如果你執行 XmlDemo 中的 main() 方法,你會發現 process 每 10 秒執行一次。為了多樣化,這個使用了 cron 表示式而不是簡單的固定延遲。因此,你會注意到時間是基於 3 秒的偏移量(:13, :23 等),當然 cron 表示式可以更強大(我只是不想建立一個僅在特定日期或時間執行的示例)。值得指出的是,對 cron 表示式排程的支援直接包含在 Spring 3.0 本身中。
一定要查閱 Spring 3.0 參考手冊的 任務執行和排程章節,以瞭解更多關於新的 TaskScheduler 抽象和 Trigger 策略的資訊,它們為你在這裡看到的內容提供了基礎。參考手冊還討論了“task”名稱空間提供的其他元素,用於配置具有特定執行緒池設定的 TaskScheduler 和 TaskExecutor 例項。
希望這篇博文對這些新功能進行了有用的概述。請繼續關注 SpringSource 團隊部落格,瞭解更多 Spring 3.0 相關內容。