Job
由於Play是Web應用框架,所以大部分的應用邏輯是由響應HTTP請求的控制器來完成的。但是有時候我們需要在HTTP請求之外執行一些應用邏輯操作,比如初始化工作,維護任務或者在不阻塞HTTP請求執行池的情況下運行一些時間花費較長的任務。這時就可以利用Play提供的Job來滿足這些需求。
Job完全由框架管理,這意味着Play會管理所有的數據庫連接,JPA實體管理器的同步以及事務
補充:
Job就是需要在指定的時刻或者時間段內執行的任務,通常由作業調度程序來執行調度和管理。
1、實現
在Play中建立Job只需要繼承play.jobs.Job類:
package jobs;
import play.jobs.*;
public class MyJob extends Job {
public void doJob() {
// 執行一些業務邏輯
}
}
如果希望創建具有返回值的Job,那麼需要覆蓋doJobWithResult()方法:
package jobs;
import play.jobs.*;
public class MyJob extends Job<String> {
public String doJobWithResult() {
// 執行一些業務邏輯
return result;
}
}
上例自定義的Job覆蓋了doJobWithResult()方法,並且方法的返回類型爲String,事實上Job可以返回任何類型的值。
2、Bootstrap
2.1 應用啓動#
Bootstrap Job,顧名思義就是在應用開始時運行的任務,只需要添加@OnApplicationStart註解就可以把當前Job設置爲Bootstrap Job:
import play.jobs.*;
@OnApplicationStart
public class Bootstrap extends Job {
public void doJob() {
if(Page.count() == 0) {
new Page("root").save();
Logger.info("A root page has been created.");
}
}
}
如果希望Web應用啓動後,能夠在執行Bootstrap Job的同時,又能很快地處理到來的請求,可以爲@OnApplicationStart註解添加async=true屬性:@OnApplicationStart(async=true)。這樣應用程序開啓後,Bootstrap Job就會作爲後臺程序異步執行了。不僅如此,所有的異步Job(async=true)也會在Web應用開啓之後同時運行。
Play具有兩種不同的工作模式:開發模式(DEV)和產品模式(PROD),因此Job的啓動時間也有略微差異。在DEV模式下,直到第一個HTTP請求到達時纔會開啓應用,且不會預先編譯Java文件。如果在該模式下更改Java源文件可以立即生效,刷新瀏覽器即可查看修改後的結果。此外,應用還會在需要的時候自動重啓;而在PROD模式下,應用會在服務器啓動時同步開啓,一旦應用開啓就會自動編譯所有的Java文件,之後不再重載任何文件(包括模板文件和配置文件),所以如果有文件修改必須重啓應用才能生效。所以DEV模式下Job會延遲啓動。
2.2 應用停止#
Web應用停止或關閉的時候,常常也需要進行一些額外的操作,如進行數據的清理、日誌的打印等。如果開發者需要這類任務調度操作,可以使用Play提供的@OnApplicationStop註解。
import play.jobs.*;
@OnApplicationStop
public class Bootstrap extends Job {
public void doJob() {
Fixture.deleteAll();
}
}
用法非常簡單,繼承Job類之後,重寫doJob()方法即可。
3、Scheduled Job
Scheduled Job是指可以被框架週期性執行的任務,可以使用@Every註解指定時間間隔控制Scheduled Job運行,例如:
import play.jobs.*;
@Every("1h")
public class Bootstrap extends Job {
public void doJob() {
List<User> newUsers = User.find("newAccount = true").fetch();
for(User user : newUsers) {
Notifier.sayWelcome(user);
}
}
}
import play.jobs.*;
/** Fire at 12pm (noon) every day **/
@On("0 0 12 * * ?")
public class Bootstrap extends Job {
public void doJob() {
Logger.info("Maintenance job ...");
...
}
}
與Bootstrap Job一樣,Scheduled Job也是不需要任何返回值的,即使返回了也會丟失。
@On標籤中使用的是Quartz庫的CRON表達式。CRON表達式是由7個子表達式組成的字符串,每個子表達式都描述了單獨的日程細節。這些子表達式用空格分隔,分別表示:
- Seconds 秒
- Minutes 分鐘
- Hours 小時
- Day-of-Month 一個月中的某一天
- Month 月
- Day-of-Week 一週中的某一天
- Year 年(可選)
具體CRON表達式的例子:"0 0 12 ? * WED",表示“每週三的中午12:00”。
4、Job的直接調用
Play的Job除了被框架自動調用外,也可以通過now()方法手動調用Job對象的實例,隨時觸發Job來執行指定任務。使用now()方法調用Job後,任務會立即執行:
public static void encodeVideo(Long videoId) {
new VideoEncoder(videoId).now();
renderText("Encoding started");
}
now()方法的返回值是Promise對象,通過該值可以在結束後獲得Job的執行結果。
Job的價值就體現在能夠無縫集成到Play應用當中,從而進一步提高了開發效率。Play針對不同的需求提供了各種形式的Job:Bootstrap Job在應用開始時執行;Scheduled Job會被框架週期性執行,本章最後還提到了如何直接在業務程序中調用Job。