搶佔先機
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" 元素指向包含我們“bean”的包。它們有兩個: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)。最後一個“開始工作”訊息和第一個“完成工作”訊息之間應該有大約 5 秒的暫停。如果所有工作都在單個執行緒中執行,我們將看到順序的開始/完成對,整個過程大約需要 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 表示式比這強大得多(我只是不想建立一個只在特定日期或時間執行的示例)。值得指出的是,Spring 3.0 本身就直接包含了對基於 cron 的排程的支援。
請務必查閱 Spring 3.0 參考手冊的任務執行和排程一章,瞭解更多關於新的 TaskScheduler 抽象和 Trigger 策略,它們為您在此處看到的內容奠定了基礎。參考手冊還討論了“task”名稱空間提供的其他元素,用於配置具有特定執行緒池設定的 TaskScheduler 和 TaskExecutor 例項。
我希望這篇文章對這些新特性提供了一個有用的概述。請繼續關注 SpringSource 團隊部落格,獲取更多與 Spring 3.0 相關的內容。