spring如何設定定時任務詳解(@Scheduled)
以前用過這個註解實現定時任務,但是隻是使用,現在做專案又用到了這個功能,系統的學習一下~
spring定時任務設定有兩種方式,註解和xml配置。推薦使用註解,在本文章也主要介紹註解方式配置
一:註解方式配置定時任務:
下面的步驟預設spring的其他配置項都已經配置好(比如啟動註解配置,包路徑掃描等)
1:在spring配置檔案中配置,新增名稱空間
- xmlns新增:
xmlns:task="http://www.springframework.org/schema/task"
- xsi:schemaLocation新增
- 注意"4.3"這是版本號,要修改和你的其他xsd版本號一致
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd"
- 啟動註解驅動
- 注意“dataScheduler”為自定義名稱,可以通過自己的業務定義 合適 的名稱
<task:annotation-driven scheduler="dataScheduler"/>
- 開啟任務排程器,並配置執行緒池大小
- 注意此處的id指定的就是上面的自定義名稱
- spring的任務排程預設是單執行緒的,如果你的專案會有多工定時執行,並且執行時間會相交的話,應該根據任務的具體執行情況配置執行緒池大小
- 如果不配置執行緒池,並且A和B任務在同一時間執行,A先執行的話,B要等待A執行完才可以執行,AB不會同時執行
<task:scheduler id="dataScheduler" pool-size="5"/>
2:使用註解配置定時任務
-
在你需要配置定時任務的方法上使用註解@Scheduled即可,下面一個簡單案例:
- 注意 下面的案例是在每天的早上2點執行
- “0 0 2 * * *”是怎麼組合的?下面會詳細介紹@Scheduled()註解
@Scheduled(cron = "0 0 2 * * *")
public void init(){
todo...
}
在此需要注意:@Scheduled只能註釋在無參的方法上,我看網上有許多部落格說必須無參無返回值的,但是經過我的測試有返回值是可以的,可能是版本更新了吧。
現在就算是完成spring定時器的使用了,下面讓我們來詳細的看一下@Scheduled註解吧~
二:@Scheduled
@Scheduled註解是Spring專門為定時任務設計的註解
首先,讓我們來看看這個註解是怎麼組成的吧(適用於版本JDK8與spring4.3及其以上)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String cron() default "";
String zone() default "";
long fixedDelay() default -1L;
String fixedDelayString() default "";
long fixedRate() default -1L;
String fixedRateString() default "";
long initialDelay() default -1L;
String initialDelayString() default "";
}
從上述程式碼中看以看出:
1:@Scheduled被註解部分:
- 元註解@Target表明@Scheduled註解可以在方法上使用(ElementType.METHOD),也可以作為元註解對其他註解進行註解(ElementType.ANNOTATION_TYPE)
- 元註解@Retention表明此註解會被JVM所保留,也就是會儲存在執行時(RetentionPolicy.RUNTIME)
- 元註解@Documented表明此註解應該被 javadoc工具記錄。預設情況下javadoc是不包括註解的。
- JDK8新增的註解@Repeatable表明此註解可以在同一個地方被重複使用
上述的所涉及到的註解有不清楚作用的,可以自行baidu\google,網上有好多介紹的文章。
2:@Scheduled引數部分,總共包含8各部分,我們來分別看一下其作用:
- cron:一個類似cron的表示式,擴充套件了通常的UN * X定義,包括秒,分,時,星期,月,年的觸發器。
- fixedDelay:在最後一次呼叫結束和下一次呼叫開始之間以固定週期(以毫秒為單位)執行帶註釋的方法。(要等待上次任務完成後)
- fixedDelayString:同上面作用一樣,只是String型別
- fixedRate:在呼叫之間以固定的週期(以毫秒為單位)執行帶註釋的方法。(不需要等待上次任務完成)
- fixedRateString:同上面作用一樣,只是String型別
- initialDelay:第一次執行fixedRate()或fixedDelay()任務之前延遲的毫秒數 。
- initialDelayString:同上面作用一樣,只是String型別
- zone:指明解析cron表示式的時區。
cron可以組合出更多的定時情況,fixedDelay和fixedRate只能定義每隔多長時間執行一次。
在上述cron、fixedDelay、fixedRate 只能同時存在一個,使用其中一個就不能使用另外的一個,否則會報錯“java.lang.IllegalStateException”
3:cron引數
一個cron表示式可以有6個元素或者7個元素組成(“年”這個元素可以省略,省略之後就是預設“每一年”)
3.1:按順序依次為:
- 秒(0~59)
- 分鐘(0~59)
- 小時(0~23)
- 天(0~31)
- 月(0~11)
- 星期(1~7 )或者( SUN,MON,TUE,WED,THU,FRI,SAT。其中SUN = 1)
- 年份(1970-2099)
3.2:每個元素可以接受的值:
欄位 | 允許值 | 允許的特殊字元 |
---|---|---|
秒 | 0-59 | , - * / |
分 | 0-59 | , - * / |
小時 | 0-23 | , - * / |
日期 | 1-31 | , - * ? / L W C |
月份 | 1-12 或者 JAN-DEC | , - * / |
星期 | 1-7 或者 SUN-SAT | , - * ? / L C # |
年 | 空, 1970-2099 | , - * / |
3.3:一些特殊字元解釋與注意事項,可以結合下面的小案例來理解:
- 其中每個元素可以是一個值(如6),一個連續區間(9-12),一個間隔時間(8-18/4)(/表示每隔4小時),一個列表(1,3,5),萬用字元。
- 其中的“日”由於"月份中的日期"和"星期"這兩個元素互斥的,必須要對其中一個設定“?”。
- 有些子表示式能包含一些範圍或列表
- 例如:子表示式(天(星期))可以為 “MON-FRI”,“MON,WED,FRI”,“MON-WED,SAT”
- “*”字元代表所有可能的值
- “/”字元用來指定數值的增量
- 例如:在子表示式(分鐘)裡的“0/15”表示從第0分鐘開始,每15分鐘
- 在子表示式(分鐘)裡的“3/20”表示從第3分鐘開始,每20分鐘(它和“3,23,43”)的含義一樣
- “?”字元僅被用於天(月)和天(星期)兩個子表示式,表示不指定值
- 當2個子表示式其中之一被指定了值以後,為了避免衝突,需要將另一個子表示式的值設為“?”
- “L” 字元僅被用於天(月)和天(星期)兩個子表示式,它是單詞“last”的縮寫
- 如果在“L”前有具體的內容,它就具有其他的含義了。例如:“6L”表示這個月的倒數第6天
- 注意:在使用“L”引數時,不要指定列表或範圍,因為這會導致問題
- “W” 字元代表著平日(Mon-Fri),並且僅能用於日域中。它用來指定離指定日的最近的一個平日。大部分的商業處理都是基於工作周的,所以 W 字元可能是非常重要的。
- 例如,日域中的 15W 意味著 “離該月15號的最近一個平日。” 假如15號是星期六,那麼 trigger 會在14號(星期五)觸發,因為星期四比星期一離15號更近。
- “C”:代表“Calendar”的意思。它的意思是計劃所關聯的日期,如果日期沒有被關聯,則相當於日曆中所有日期。例如5C在日期欄位中就相當於日曆5日以後的第一天。1C在星期欄位中相當於星期日後的第一天。
3.4:一些小案例:
- “0 0 10,14,16 * * ?” 每天上午10點,下午2點,4點
- “0 0/30 9-17 * * ?” 朝九晚五工作時間內每半小時
- “0 0 12 ? * WED” 表示每個星期三中午12點
- “0 0 12 * * ?” 每天中午12點觸發
- “0 15 10 ? * *” 每天上午10:15觸發(這個和下一個案例說明,必須"月份中的日期"和"星期"中有一個設定為“?”)
- “0 15 10 * * ?” 每天上午10:15觸發
- “0 15 10 * * ? *” 每天上午10:15觸發(7個元素型別案例,第七個元素代表年)
- “0 15 10 * * ? 2005” 2005年的每天上午10:15觸發
- “0 * 14 * * ?” 在每天下午2點到下午2:59期間的每1分鐘觸發
- “0 0/5 14 * * ?” 在每天下午2點到下午2:55期間的每5分鐘觸發
- “0 0/5 14,18 * * ?” 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
- “0 0-5 14 * * ?” 在每天下午2點到下午2:05期間的每1分鐘觸發
- “0 10,44 14 ? 3 WED” 每年三月的星期三的下午2:10和2:44觸發
- “0 15 10 ? * MON-FRI” 週一至週五的上午10:15觸發
- “0 15 10 15 * ?” 每月15日上午10:15觸發
- “0 15 10 L * ?” 每月最後一日的上午10:15觸發
- “0 15 10 ? * 6L” 每月的最後一個星期五上午10:15觸發
- “0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最後一個星期五上午10:15觸發
- “0 15 10 ? * 6#3” 每月的第三個星期五上午10:15觸發
到這個地方你應該對@Scheduled有一個較全面的理解了,下面我們就來簡單的看一下其實現原理吧~
三:原理簡介
1:主要過程:
-
spring在使用applicationContext將類全部初始化。
-
呼叫ScheduledAnnotationBeanPostProcessor類中的postProcessAfterInitialization方法獲取專案中所有被註解 @Scheduled註解的方法 。
-
通過processScheduled方法將所有定時的方法存放在Set tasks = new LinkedHashSet(4); 定時任務佇列中,並解析相應的引數。順序存放,任務也是順序執行。存放順序為cron>fixedDelay>fixedRate
-
將解析引數後的定時任務存放在一個初始容量為16 的map中,key為bean name,value為定時任務:private final Map<Object, Set> scheduledTasks = new IdentityHashMap(16);
-
之後交給ScheduledTaskRegistrar類的方法scheduleTasks去新增定時任務。
2:上述就是一個大致過程,下面看一下相應的原始碼:
注意 :spring對定時任務的操作的原始碼全部在spring-context.jar包下的org.springframework.scheduling包下面,主要包含三部分:annotation、config、 support,大家有興趣的話可以去看看
1:獲取專案中所有被註解 @Scheduled註解的方法
public Object postProcessAfterInitialization(Object bean, String beanName) {
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) {
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MetadataLookup<Set<Scheduled>>() {
public Set<Scheduled> inspect(Method method) {
//獲取註解方法
**Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);**
return !scheduledMethods.isEmpty() ? scheduledMethods : null;
}
});
if (annotatedMethods.isEmpty()) {
...
} else {
Iterator var5 = annotatedMethods.entrySet().iterator();
while(var5.hasNext()) {
Entry<Method, Set<Scheduled>> entry = (Entry)var5.next();
Method method = (Method)entry.getKey();
Iterator var8 = ((Set)entry.getValue()).iterator();
while(var8.hasNext()) {
Scheduled scheduled = (Scheduled)var8.next();
//將獲取的任務進行引數解析並存放到任務佇列
this.processScheduled(scheduled, method, bean);
}
}
...
}
}
return bean;
}
2:通過processScheduled方法將所有定時的方法存放在定時任務佇列中
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
try {
...
//解析initialDelayString引數
String initialDelayString = scheduled.initialDelayString();
if (StringUtils.hasText(initialDelayString)) {
...
}
//解析cron引數
String cron = scheduled.cron();
if (StringUtils.hasText(cron)) {
...
//存放到任務佇列中
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
}
...
//解析fixedDelay引數
long fixedDelay = scheduled.fixedDelay();
if (fixedDelay >= 0L) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
}
String fixedDelayString = scheduled.fixedDelayString();
if (StringUtils.hasText(fixedDelayString)) {
...
//存放到任務佇列中
tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
}
//解析fixedRate引數
long fixedRate = scheduled.fixedRate();
if (fixedRate >= 0L) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
}
String fixedRateString = scheduled.fixedRateString();
if (StringUtils.hasText(fixedRateString)) {
...
//存放到任務佇列中
tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
}
Assert.isTrue(processedSchedule, errorMessage);
Map var19 = this.scheduledTasks;
//併發控制並將任務存放在map中
synchronized(this.scheduledTasks) {
Set<ScheduledTask> registeredTasks = (Set)this.scheduledTasks.get(bean);
if (registeredTasks == null) {
registeredTasks = new LinkedHashSet(4);
//將任務存放在map中
this.scheduledTasks.put(bean, registeredTasks);
}
((Set)registeredTasks).addAll(tasks);
}
} catch (IllegalArgumentException var26) {
throw new IllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " + var26.getMessage());
}
}
3:之後交給ScheduledTaskRegistrar類的方法scheduleTasks去新增定時任務
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
Iterator var1;
if (this.triggerTasks != null) {
var1 = this.triggerTasks.iterator();
while(var1.hasNext()) {
TriggerTask task = (TriggerTask)var1.next();
this.addScheduledTask(this.scheduleTriggerTask(task));
}
}
if (this.cronTasks != null) {
var1 = this.cronTasks.iterator();
while(var1.hasNext()) {
CronTask task = (CronTask)var1.next();
this.addScheduledTask(this.scheduleCronTask(task));
}
}
IntervalTask task;
if (this.fixedRateTasks != null) {
var1 = this.fixedRateTasks.iterator();
while(var1.hasNext()) {
task = (IntervalTask)var1.next();
this.addScheduledTask(this.scheduleFixedRateTask(task));
}
}
if (this.fixedDelayTasks != null) {
var1 = this.fixedDelayTasks.iterator();
while(var1.hasNext()) {
task = (IntervalTask)var1.next();
this.addScheduledTask(this.scheduleFixedDelayTask(task));
}
}
}
此部分只是對原理進行了簡單的介紹,如果有興趣深入瞭解,可以去看看原始碼~
四:其他
做定時任務還可以使用java自帶的原生API,Timer和TimerTask去設計。
-
Timer:一種工具,執行緒用其安排以後在後臺執行緒中執行的任務。可安排任務執行一次,或者定期重複執行。
-
TimerTask:定義一個被執行的任務,Timer 安排該任務為一次執行或重複執行的任務。
可以這樣理解Timer是一種定時器工具,用來在一個後臺執行緒計劃執行指定任務,而TimerTask一個抽象類,它的子類代表一個可以被Timer計劃的任務。
這裡就簡單的提一下,並不是本文的重點,具體的用法自行google吧~
如果轉載此博文,請附上本文連結:https://blog.csdn.net/CSDN___LYY/article/details/85266567 謝謝合作~
如果感覺這篇文章對您有所幫助,請點選一下喜歡或者關注博主,您的喜歡和關注將是我前進的最大動力!
相關文章
- 定時任務@Scheduled引數詳解
- js設定定時任務JS
- Spring 定時任務Scheduled 開發詳細圖文Spring
- Spring Boot之使用Scheduled註解實現定時任務Springboot
- @Scheduled 定時任務
- Linux(Debian)使用crontab設定定時任務Linux
- SpringBoot 定時任務ScheduledSpring Boot
- SpingBoot @Scheduled定時任務boot
- springboot定時任務@ScheduledSpring Boot
- Spring Boot入門(三):使用Scheduled註解實現定時任務Spring Boot
- Spring Boot系列之使用@Scheduled實現定時任務Spring Boot
- @Scheduled 定時任務自定義
- Jenkins設定定時觸發器執行任務Jenkins觸發器
- SpringBoot執行定時任務@ScheduledSpring Boot
- win10如何建立任務計劃_win10電腦怎麼設定定時任務Win10
- 【親測有效】【定時】定時任務 @Scheduled(cron = "0 0 21 * * ?") 【Scheduled失效】
- spring定時任務註解Spring
- springcloud +springboot 社交電子商務-定時任務@ScheduledGCCloudSpring Boot
- 你不知道的Scheduled定時任務騷操作
- NAS如何設定定時開關機
- CentOS7設定定時任務 每隔30分鐘執行一次命令CentOS
- 玩轉SpringBoot之定時任務@Scheduled執行緒池配置Spring Boot執行緒
- Spring - Task定時任務Spring
- win10如何設定定時開啟bat win10設定定時開啟bat方法Win10BAT
- win10如何簡單設定定時關機 電腦怎麼設定定時關機Win10
- Quartz定時任務框架(二) Quartz詳解quartz框架
- 定時任務報警通知解決方案詳解
- spring - mvc - @ScheduledSpringMVC
- 日誌切割logrotate和定時任務crontab詳解logrotate
- Spring Boot中引入定時任務Spring Boot
- spring boot中的定時任務Spring Boot
- Spring Boot 配置 Quartz 定時任務Spring Bootquartz
- 伺服器設定定時器伺服器定時器
- 定時ftp上傳,如何設定定時ftp上傳檔案FTP
- Linux下的crontab定時執行任務命令詳解Linux
- Centos 7下利用crontab定時執行任務詳解CentOS
- Spring排程定時任務的方式Spring
- Spring定時任務高階使用篇Spring