java 中Spring task定時任務(wù)的深入理解
java 中Spring task定時任務(wù)的深入理解
在工作中有用到spring task作為定時任務(wù)的處理,spring通過接口TaskExecutor和TaskScheduler這兩個接口的方式為異步定時任務(wù)提供了一種抽象。這就意味著spring容許你使用其他的定時任務(wù)框架,當(dāng)然spring自身也提供了一種定時任務(wù)的實(shí)現(xiàn):spring task。spring task支持線程池,可以高效處理許多不同的定時任務(wù)。同時,spring還支持使用Java自帶的Timer定時器和Quartz定時框架。限于篇幅,這里將只介紹spring task的使用。
其實(shí),官方文檔已經(jīng)介紹地足夠詳細(xì),只不過都是英文版,所以為了更好地理解并使用spring task,首先會對spring task的實(shí)現(xiàn)原理做一個簡單的介紹,然后通過實(shí)際代碼演示spring task是如何使用的。這里會涉及到一個很重要的知識點(diǎn):cron表達(dá)式。
TaskExecutor和TaskScheduler
TaskExecutor是spring task的第一個抽象,它很自然讓人聯(lián)想到j(luò)dk中concurrent包下的Executor,實(shí)際上TaskExecutor就是為區(qū)別于Executor才引入的,而引入TaskExecutor的目的就是為定時任務(wù)的執(zhí)行提供線程池的支持,那么,問題來了,為什么spring不直接使用jdk自帶的Executor呢?TaskExecutor源碼如下?
public interface TaskExecutor extends Executor { void execute(Runnable var1); }
那么,答案很顯然,TaskExecutor提供的線程池支持也是基于jdk自帶的Executor的。用法于Executor沒有什么不同。
TaskScheduler是spring task的第二個抽象,那么從字面的意義看,TaskScheduler就是為了提供定時任務(wù)的支持咯。TaskScheduler需要傳入一個Runnable的任務(wù)做為參數(shù),并指定需要周期執(zhí)行的時間或者觸發(fā)器,這樣Runnable任務(wù)就可以周期性執(zhí)行了。傳入時間很好理解,有意思的是傳入一個觸發(fā)器(Trigger)的情況,因為這里需要使用cron表達(dá)式去觸發(fā)一個定時任務(wù),所以有必要先了解下cron表達(dá)式的使用。
在spring 4.x中已經(jīng)不支持7個參數(shù)的cronin表達(dá)式了,要求必須是6個參數(shù)(具體哪個參數(shù)后面會說)。cron表達(dá)式的格式如下:
{秒} {分} {時} {日期(具體哪天)} {月} {星期}
- 秒:必填項,允許的值范圍是0-59,支持的特殊符號包括
- , - * /,,表示特定的某一秒才會觸發(fā)任務(wù),-表示一段時間內(nèi)會觸發(fā)任務(wù),*表示每一秒都會觸發(fā),/表示從哪一個時刻開始,每隔多長時間觸發(fā)一次任務(wù)。
- 分:必填項,允許的值范圍是0-59,支持的特殊符號和秒一樣,含義類推
- 時:必填項,允許的值范圍是0-23,支持的特殊符號和秒一樣,含義類推
- 日期:必填項,允許的值范圍是1-31,支持的特殊符號相比秒多了?,表示與{星期}互斥,即意味著若明確指定{星期}觸發(fā),則表示{日期}無意義,以免引起沖突和混亂。
- 月:必填項,允許的值范圍是1-12(JAN-DEC),支持的特殊符號與秒一樣,含義類推
- 星期:必填項,允許值范圍是1~7 (SUN-SAT),1代表星期天(一星期的第一天),以此類推,7代表星期六,支持的符號相比秒多了?,表達(dá)的含義是與{日期}互斥,即意味著若明確指定{日期}觸發(fā),則表示{星期}無意義。
比如下面這個cron表達(dá)式:
// 表達(dá)的含義是:每半分鐘觸發(fā)一次任務(wù) 30 * * * * ?
spring提供了一個CronTrigger,通過傳入一個Runnable任務(wù)和CronTrigger,就可以使用cron表達(dá)式去指定定時任務(wù)了,是不是非常方面。實(shí)際上,在工程實(shí)踐上,cron表達(dá)式也是使用很多的。實(shí)際上,是執(zhí)行了下面的代碼:
scheduler.schedule(task, new CronTrigger("30 * * * * ?"));
TaskScheduler抽象的好處是讓需要執(zhí)行定時任務(wù)的代碼不需要指定特定的定時框架(比如Timer和Quartz)。TaskScheduler的更簡單的實(shí)現(xiàn)是ThreadPoolTaskScheduler,它實(shí)際上代理一個jdk中的SchedulingTaskExecutor,并且也實(shí)現(xiàn)了TaskExecutor接口,所以需要經(jīng)常執(zhí)行定時任務(wù)的場景可以使用這個實(shí)現(xiàn)(Spring推薦)。我們再來看一下TaskExecutor和TaskScheduler的類繼承關(guān)系:
通常而言,使用spring task實(shí)現(xiàn)定時任務(wù)有兩種方式:注解和xml配置文件。這里使用xml配置文件的方式加以說明。
實(shí)戰(zhàn)
創(chuàng)建Maven工程,pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <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.rhwayfun</groupId> <artifactId>sring-task-demo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
開發(fā)需要執(zhí)行定時任務(wù)的方法:
package com.rhwayfun.task; import org.springframework.stereotype.Component; import java.time.LocalDateTime; /** * @author ZhongCB * @date 2016年09月10日 14:30 * @description */ @Component public class App { public void execute1(){ System.out.printf("Task: %s, Current time: %s\n", 1, LocalDateTime.now()); } public void execute2(){ System.out.printf("Task: %s, Current time: %s\n", 2, LocalDateTime.now()); } public void execute3(){ System.out.printf("Task: %s, Current time: %s\n", 3, LocalDateTime.now()); } public void execute4(){ System.out.printf("Task: %s, Current time: %s\n", 4, LocalDateTime.now()); } public void execute5(){ System.out.printf("Task: %s, Current time: %s\n", 5, LocalDateTime.now()); } public void execute6(){ System.out.printf("Task: %s, Current time: %s\n", 6, LocalDateTime.now()); } public void execute7(){ System.out.printf("Task: %s, Current time: %s\n", 7, LocalDateTime.now()); } public void execute8(){ System.out.printf("Task: %s, Current time: %s\n", 8, LocalDateTime.now()); } public void execute9(){ System.out.printf("Task: %s, Current time: %s\n", 9, LocalDateTime.now()); } public void execute10(){ System.out.printf("Task: %s, Current time: %s\n", 10, LocalDateTime.now()); } public void execute11(){ System.out.printf("Task: %s, Current time: %s\n", 11, LocalDateTime.now()); } }
spring配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd"> <!-- 配置注解掃描 --> <context:component-scan base-package="com.rhwayfun.task"/> <task:scheduler id="taskScheduler" pool-size="100" /> <task:scheduled-tasks scheduler="taskScheduler"> <!-- 每半分鐘觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute1" cron="30 * * * * ?"/> <!-- 每小時的10分30秒觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute2" cron="30 10 * * * ?"/> <!-- 每天1點(diǎn)10分30秒觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute3" cron="30 10 1 * * ?"/> <!-- 每月20號的1點(diǎn)10分30秒觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute4" cron="30 10 1 20 * ?"/> <!-- 每年10月20號的1點(diǎn)10分30秒觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute5" cron="30 10 1 20 10 ?"/> <!-- 每15秒、30秒、45秒時觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute6" cron="15,30,45 * * * * ?"/> <!-- 15秒到45秒每隔1秒觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute7" cron="15-45 * * * * ?"/> <!-- 每分鐘的每15秒時任務(wù)任務(wù),每隔5秒觸發(fā)一次 --> <task:scheduled ref="app" method="execute8" cron="15/5 * * * * ?"/> <!-- 每分鐘的15到30秒之間開始觸發(fā),每隔5秒觸發(fā)一次 --> <task:scheduled ref="app" method="execute9" cron="15-30/5 * * * * ?"/> <!-- 每小時的0分0秒開始觸發(fā),每隔3分鐘觸發(fā)一次 --> <task:scheduled ref="app" method="execute10" cron="0 0/3 * * * ?"/> <!-- 星期一到星期五的10點(diǎn)15分0秒觸發(fā)任務(wù) --> <task:scheduled ref="app" method="execute11" cron="0 15 10 ? * MON-FRI"/> </task:scheduled-tasks> </beans>
編寫測試代碼:
package com.rhwayfun.task; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author ZhongCB * @date 2016年09月10日 14:55 * @description */ public class AppTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/app-context-task.xml"); } }
運(yùn)行測試代碼,控制臺會定時輸出每個定時任務(wù)的日志信息,說明測試通過。
小插曲
由于項目使用jdk 1.8進(jìn)行開發(fā),所以初始的時候每次pom文件發(fā)生修改,編譯器的版本又變成了jdk 1.5,后面發(fā)現(xiàn)需要在pom文件中添加build便簽?zāi)遣糠植拍軐⒛J(rèn)的編譯器進(jìn)行修改。也算一個小收獲了。
如有疑問請留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
JAVA (Jsp)利用Google的Translate開發(fā)API的代碼
上次我說了用ASP,PHP,C#分別實(shí)現(xiàn)Google的Translate開發(fā)一個API ( Tags:Google Translate API )2008-12-12JSP中獲取ExtJS.Ajax前臺傳遞的JSON數(shù)據(jù)實(shí)現(xiàn)過程
JSON數(shù)據(jù)接收的特定過程必須的數(shù)據(jù)包:commons-lang,commons-beanutils等等,否則JSONObject 報錯,并且不能接收,感興趣的朋友可以參考下哈2013-04-04Java Web實(shí)現(xiàn)的基本MVC實(shí)例分析
這篇文章主要介紹了Java Web實(shí)現(xiàn)的基本MVC,以完整實(shí)例形式較為詳細(xì)的分析了JSP實(shí)現(xiàn)MVC架構(gòu)的具體步驟與相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09二、設(shè)置開發(fā)、運(yùn)行環(huán)境
二、設(shè)置開發(fā)、運(yùn)行環(huán)境...2006-10-10JAVA/JSP學(xué)習(xí)系列之一(JDK安裝)
JAVA/JSP學(xué)習(xí)系列之一(JDK安裝)...2006-10-10tomcat6下jsp出現(xiàn)getOutputStream() has already been called for t
這篇文章主要介紹tomcat6下jsp出現(xiàn)getOutputStream() has already been called for this response異常的原因和解決方法,需要的朋友可以參考下。2016-06-06JSP中include指令和include行為的區(qū)別
我們都知道在jsp中include有兩種形式,分別是2008-07-07