springboot實現(xiàn)定時任務(wù)的四種方式小結(jié)
因為某些需求,要在特定的時間執(zhí)行一些任務(wù),比如定時刪除服務(wù)器存儲的數(shù)據(jù)緩存,定時獲取數(shù)據(jù)以及定時發(fā)送推送等等,這時就需要用到定時任務(wù)了。定時任務(wù),指的是在編程過程中無須做復(fù)雜控制的前提下執(zhí)行簡單的定時操作。
Timer
在java中一個完整的定時任務(wù)可以用Timer和TimerTask兩個類配合完成。
Timer是一種工具,線程用其安排在后臺線程中執(zhí)行的任務(wù),可安排任務(wù)執(zhí)行一次或者定期重復(fù)執(zhí)行。
TimerTask是由Timer安排執(zhí)行一次或者重復(fù)執(zhí)行的任務(wù)。
Timer中提供了四個方法:
(1)schedule(TimerTask task,Date time)——安排在指定的時間執(zhí)行指定的任務(wù)
(2)schedule(TimerTask task,Date firstTime,long period)——安排指定的任務(wù)在指定的時間開始進行重復(fù)的固定延遲執(zhí)行
(3)schedule(TimerTask task,long delay)——安排在指定延遲后執(zhí)行指定的任務(wù)
(4)schedule(TimerTask task,long delay,long period)——安排指定的任務(wù)在指定的延遲后開始進行重復(fù)的固定速率執(zhí)行
示例:
首先需要創(chuàng)建一個類作為定時任務(wù),該類需要繼承TimerTask
public class TimerTask extends java.util.TimerTask{? ?? ?@Override ?? ?public void run() { ?? ??? ?//這里執(zhí)行定時任務(wù)內(nèi)容 ?? ??? ?System.out.println("+++++++"); ?? ?}?? ? }
然后創(chuàng)建Timer調(diào)用之前創(chuàng)建的定時任務(wù)
public class TimerTest { ?? ? ?? ?public static void main(String[] args) { ?? ??? ?Timer timer = new Timer(); ?? ??? ?TimerTask noticeTask = new TimerTask(); ?? ??? ?timer.schedule(noticeTask,0,2000); ?? ??? ?timer.cancel(); ?? ??? ?System.out.println("結(jié)束");?? ? ?? ?} ? }
這樣定時執(zhí)行任務(wù)的功能就實現(xiàn)了,但Timer有著一定的缺陷:
Timer對于系統(tǒng)時間的改變非常敏感,它對調(diào)度的支持是基于絕對時間而不是相對時間。
Timer線程是不會捕獲異常的,多線程并行處理定時任務(wù)時,Timer運行多個TimerTask時,只要其中之一沒有捕獲拋出的異常,其他任務(wù)便會自動終止運行。同時Timer也不會重新恢復(fù)線程的執(zhí)行,它會錯誤的認為整個Timer線程都會取消,已經(jīng)被安排但尚未執(zhí)行的TimerTask也不會再執(zhí)行了,新的任務(wù)也不能被調(diào)度。因此,如果TimerTask拋出未檢查的異常,Timer將會產(chǎn)生無法預(yù)料的行為。
ScheduledExecutor
Timer是基于絕對時間的,對系統(tǒng)時間比較敏感,而ScheduledExecutor則是基于相對時間。
Timer的內(nèi)部只有一個線程,如果有多個任務(wù)的話就會順序執(zhí)行,這樣我們的延遲時間和循環(huán)時間就會出現(xiàn)問題。而ScheduledThreadPoolExecutor內(nèi)部是個線程池,可以支持多個任務(wù)并發(fā)執(zhí)行,在對延遲任務(wù)和循環(huán)任務(wù)要求嚴格的時候,就需要考慮使用ScheduledExecutor了。
針對Timer類存在的缺陷,Java 5 推出了基于線程池設(shè)計的 ScheduledExecutor,ScheduledExecutor的設(shè)計思想是每一個被調(diào)度的任務(wù)都會由線程池中一個線程去執(zhí)行,因此任務(wù)是并發(fā)的,相互之間不會受到干擾,只有當(dāng)任務(wù)的時間到來時,ScheduledExecutor才會真正啟動一個線程,其余時間ScheduledExecutor都是處于輪詢?nèi)蝿?wù)的狀態(tài)。如果我們設(shè)定的調(diào)度周期小于任務(wù)運行時間,該任務(wù)會被重復(fù)添加到一個延時任務(wù)隊列,所以同一時間任務(wù)隊列中會有多個任務(wù)待調(diào)度,線程池會首先獲取優(yōu)先級高的任務(wù)執(zhí)行。效果就是任務(wù)運行多長時間,調(diào)度時間就會變?yōu)槎嗑?,因為添加到任?wù)隊列的任務(wù)的延時時間每次都是負數(shù),所以會被立刻執(zhí)行。
示例:
public class MyScheduledExecutor implements Runnable{ ?? ? ? ? private String jobName; ? ?? ? ? MyScheduledExecutor() { ? ? ? ?? ? ? } ? ?? ? ? MyScheduledExecutor(String jobName) { ? ? ? ? this.jobName = jobName; ? ? } ? ? ? @Override ? ? public void run() { ?? ? ? ?System.out.println(jobName + " is running"); ? ? } ? }
public class MyScheduledExecutorService { public static void main(String[] args) { long initialDelay = 3; long period = 1; /** * 創(chuàng)建一個線程池,它可安排在給定延遲后運行任務(wù)或者定期地執(zhí)行任務(wù) * 參數(shù):corePoolSize - 池中所保存的線程數(shù),即使線程是空閑的也包括在內(nèi) */ ScheduledExecutorService service = Executors.newScheduledThreadPool(2); /** * 從現(xiàn)在開始3秒鐘之后,每隔1秒鐘執(zhí)行一次job1,ScheduleAtFixedRate是基于固定時間間隔進行任務(wù)調(diào)度 * 參數(shù):1、任務(wù)體 2、首次執(zhí)行的延時時間 * 3、任務(wù)執(zhí)行間隔 4、間隔時間單位 */ service.scheduleAtFixedRate(new MyScheduledExecutor("job1"), initialDelay, period, TimeUnit.SECONDS); /** * 從現(xiàn)在開始3秒鐘之后,每隔1秒鐘執(zhí)行一次job2,ScheduleWithFixedDelay 取決于每次任務(wù)執(zhí)行的時間長短,基于不固定時間間隔進行任務(wù)調(diào)度 */ service.scheduleWithFixedDelay(new MyScheduledExecutor("job2"), initialDelay, period, TimeUnit.SECONDS); } }
ScheduledExecutor 配合 Calendar 實現(xiàn)復(fù)雜任務(wù)調(diào)度
示例:設(shè)置每星期二的 18:30:00 執(zhí)行任務(wù)
使用 ScheduledExcetuor 和 Calendar 進行任務(wù)調(diào)度
public class ScheduledExceutorTest2 extends TimerTask { ? ?? ?private String jobName = ""; ? ?? ?public ScheduledExceutorTest2(String jobName) { ?? ??? ?super(); ?? ??? ?this.jobName = jobName; ?? ?} ? ?? ?@Override ?? ?public void run() { ?? ??? ?System.out.println("Date = " + new Date() + ", execute " + jobName); ?? ?} ? ?? ?/** ?? ? * 計算從當(dāng)前時間currentDate開始,滿足條件dayOfWeek, hourOfDay, minuteOfHour, ?? ? * secondOfMinite的最近時間 ?? ? */ ?? ?public Calendar getEarliestDate(Calendar currentDate, int dayOfWeek, int hourOfDay, int minuteOfHour, ?? ??? ??? ?int secondOfMinite) { ?? ??? ?// 計算當(dāng)前時間的WEEK_OF_YEAR,DAY_OF_WEEK, HOUR_OF_DAY, MINUTE,SECOND等各個字段值 ?? ??? ?int currentWeekOfYear = currentDate.get(Calendar.WEEK_OF_YEAR); ?? ??? ?int currentDayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK); ?? ??? ?int currentHour = currentDate.get(Calendar.HOUR_OF_DAY); ?? ??? ?int currentMinute = currentDate.get(Calendar.MINUTE); ?? ??? ?int currentSecond = currentDate.get(Calendar.SECOND); ?? ??? ?// 如果輸入條件中的dayOfWeek小于當(dāng)前日期的dayOfWeek,則WEEK_OF_YEAR需要推遲一周 ?? ??? ?boolean weekLater = false; ?? ??? ?if (dayOfWeek < currentDayOfWeek) { ?? ??? ??? ?weekLater = true; ?? ??? ?} else if (dayOfWeek == currentDayOfWeek) { ?? ??? ??? ?// 當(dāng)輸入條件與當(dāng)前日期的dayOfWeek相等時,如果輸入條件中的 ?? ??? ??? ?// hourOfDay小于當(dāng)前日期的 ?? ??? ??? ?// currentHour,則WEEK_OF_YEAR需要推遲一周 ?? ??? ??? ?if (hourOfDay < currentHour) { ?? ??? ??? ??? ?weekLater = true; ?? ??? ??? ?} else if (hourOfDay == currentHour) { ?? ??? ??? ??? ?// 當(dāng)輸入條件與當(dāng)前日期的dayOfWeek, hourOfDay相等時, ?? ??? ??? ??? ?// 如果輸入條件中的minuteOfHour小于當(dāng)前日期的 ?? ??? ??? ??? ?// currentMinute,則WEEK_OF_YEAR需要推遲一周 ?? ??? ??? ??? ?if (minuteOfHour < currentMinute) { ?? ??? ??? ??? ??? ?weekLater = true; ?? ??? ??? ??? ?} else if (minuteOfHour == currentSecond) { ?? ??? ??? ??? ??? ?// 當(dāng)輸入條件與當(dāng)前日期的dayOfWeek, hourOfDay, ?? ??? ??? ??? ??? ?// minuteOfHour相等時,如果輸入條件中的 ?? ??? ??? ??? ??? ?// secondOfMinite小于當(dāng)前日期的currentSecond, ?? ??? ??? ??? ??? ?// 則WEEK_OF_YEAR需要推遲一周 ?? ??? ??? ??? ??? ?if (secondOfMinite < currentSecond) { ?? ??? ??? ??? ??? ??? ?weekLater = true; ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ?} ?? ??? ?if (weekLater) { ?? ??? ??? ?// 設(shè)置當(dāng)前日期中的WEEK_OF_YEAR為當(dāng)前周推遲一周 ?? ??? ??? ?currentDate.set(Calendar.WEEK_OF_YEAR, currentWeekOfYear + 1); ?? ??? ?} ?? ??? ?// 設(shè)置當(dāng)前日期中的DAY_OF_WEEK,HOUR_OF_DAY,MINUTE,SECOND為輸入條件中的值。 ?? ??? ?currentDate.set(Calendar.DAY_OF_WEEK, dayOfWeek); ?? ??? ?currentDate.set(Calendar.HOUR_OF_DAY, hourOfDay); ?? ??? ?currentDate.set(Calendar.MINUTE, minuteOfHour); ?? ??? ?currentDate.set(Calendar.SECOND, secondOfMinite); ?? ??? ?return currentDate; ?? ?} ? ?? ?public static void main(String[] args) throws Exception { ?? ??? ?ScheduledExceutorTest2 test = new ScheduledExceutorTest2("job1"); ?? ??? ?// 獲取當(dāng)前時間 ?? ??? ?Calendar currentDate = Calendar.getInstance(); ?? ??? ?long currentDateLong = currentDate.getTime().getTime(); ?? ??? ?System.out.println("Current Date = " + currentDate.getTime().toString()); ?? ??? ?// 計算滿足條件的最近一次執(zhí)行時間 ?? ??? ?Calendar earliestDate = test.getEarliestDate(currentDate, 3, 18, 30, 00); ?? ??? ?long earliestDateLong = earliestDate.getTime().getTime(); ?? ??? ?System.out.println("Earliest Date = " + earliestDate.getTime().toString()); ?? ??? ?// 計算從當(dāng)前時間到最近一次執(zhí)行時間的時間間隔 ?? ??? ?long delay = earliestDateLong - currentDateLong; ?? ??? ?// 計算執(zhí)行周期為一星期 ?? ??? ?long period = 7 * 24 * 60 * 60 * 1000; ?? ??? ?ScheduledExecutorService service = Executors.newScheduledThreadPool(10); ?? ??? ?// 從現(xiàn)在開始delay毫秒之后,每隔一星期執(zhí)行一次job1 ?? ??? ?service.scheduleAtFixedRate(test, delay, period, TimeUnit.MILLISECONDS); ?? ?} ? }
其核心在于根據(jù)當(dāng)前時間推算出最近一個星期二 18:30:00 的絕對時間,然后計算與當(dāng)前時間的時間差,作為調(diào)用 ScheduledExceutor 函數(shù)的參數(shù),計算最近時間要用到 java.util.calendar 的功能。
注解@Scheduled
Spring提供的注解,優(yōu)點就是配置簡單,依賴少,缺點是同一個task,如果前一個還沒跑完后面一個就不會觸發(fā),不同的task也不能同時運行。因為scheduler的默認線程數(shù)為1,配置pool-size為2的話,會導(dǎo)致同一個task前一個還沒跑完后面又被觸發(fā)的問題,不支持集群等。
示例:
yml文件配置:
time: ? ? cron: 0/5 * * * * * ? ? interval: 5
啟動類添加@EnableScheduling
定時任務(wù):
@Component public class TimeTask { ?? ? ?? ?@Scheduled(cron = "${time.cron}") ?? ?public void ?flush1() throws ?Exception{ ? ? ? ? ? ? System.out.println("Execute1"); ? ? ? ? } ?? ? ?? ?@Scheduled(cron = "0/${time.interval} * * * * ?") ?? ?public void ?flush2() throws ?Exception{ ? ? ? ? ? ? System.out.println("Execute2"); ? ? ? ? } ?? ? ?? ?@Scheduled(cron = "0/5 * * * * ?") ?? ?public void ?flush3() throws ?Exception{ ? ? ? ? ? ? System.out.println("Execute3"); ? ? ? ? } ? }
參數(shù)介紹:
fixedDelay
上一次執(zhí)行完畢時間點之后多長時間再執(zhí)行。如:
@Scheduled(fixedDelay = 5000) //上一次執(zhí)行完畢時間點之后5秒再執(zhí)行
fixedDelayString
與 fixedDelay 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。如:
@Scheduled(fixedDelayString = "5000") //上一次執(zhí)行完畢時間點之后5秒再執(zhí)行
占位符的使用:
@Scheduled(fixedDelayString = "${time.fixedDelay}") void testFixedDelayString() { System.out.println("Execute”); }
fixedRate
上一次開始執(zhí)行時間點之后多長時間再執(zhí)行。如:
@Scheduled(fixedRate = 5000) //上一次開始執(zhí)行時間點之后5秒再執(zhí)行
fixedRateString
與 fixedRate 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。
initialDelay
第一次延遲多長時間后再執(zhí)行。如:
@Scheduled(initialDelay=1000, fixedRate=5000) //第一次延遲1秒后執(zhí)行,之后按fixedRate的規(guī)則每5秒執(zhí)行一次
initialDelayString
與 initialDelayString 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。
cron表達式
cron表達式是一個字符串,字符串以5或6個空格隔開,分成6或7個域,每一個域代表一個含義。
cron表達式語法:
[秒] [分] [小時] [日] [月] [周] [年]
注:[年]不是必須的域,可以省略[年],則一共6個域
其中各個域的定義如下:
序號 | 說明 | 必填 | 允許填寫的值 | 允許的通配符 |
1 | 秒 | 是 | 0-59 | , - * / |
2 | 分 | 是 | 0-59 | , - * / |
3 | 時 | 是 | 0-23 | , - * / |
4 | 日 | 是 | 1月31日 | , - * ? / L W |
5 | 月 | 是 | 1-12 / JAN-DEC | , - * / |
6 | 周 | 是 | 1-7 / SUN-SAT | , - * ? / L # |
7 | 年 | 否 | 1970-2099 | , - * / |
Cron表達式對特殊字符的大小寫不敏感,對代表星期的縮寫英文大小寫也不敏感。星號(*):可用在所有字段中,表示對應(yīng)時間域的每一個時刻,例如, 在分鐘字段時,表示“每分鐘”;
問號(?):該字符只在日期和星期字段中使用,它通常指定為“無意義的值”,相當(dāng)于點位符;
- 減號(-):表達一個范圍,如在小時字段中使用“10-12”,則表示從10到12點,即10,11,12;
- 逗號(,):表達一個列表值,如在星期字段中使用“MON,WED,FRI”,則表示星期一,星期三和星期五;
- 斜杠(/):x/y表達一個等步長序列,x為起始值,y為增量步長值。如在分鐘字段中使用0/15,則表示為0,15,30和45秒,而5/15在分鐘字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
- L:該字符只在日期和星期字段中使用,代表“Last”的意思,但它在兩個字段中意思不同。L在日期字段中,表示這個月份的最后一天,如一月的31號,非閏年二月的28號;如果L用在星期中,則表示星期六,等同于7。但是,如果L出現(xiàn)在星期字段里,而且在前面有一個數(shù)值X,則表示“這個月的最后X天”,例如,6L表示該月的最后星期五;
- W:該字符只能出現(xiàn)在日期字段里,是對前導(dǎo)日期的修飾,表示離該日期最近的工作日。例如15W表示離該月15號最近的工作日,如果該月15號是星期六,則匹配14號星期五;如果15日是星期日,則匹配16號星期一;如果15號是星期二,那結(jié)果就是15號星期二。但必須注意關(guān)聯(lián)的匹配日期不能夠跨月,如你指定1W,如果1號是星期六,結(jié)果匹配的是3號星期一,而非上個月最后的那天。W字符串只能指定單一日期,而不能指定日期范圍;
- LW組合:在日期字段可以組合使用LW,它的意思是當(dāng)月的最后一個工作日;
- 井號(#):該字符只能在星期字段中使用,表示當(dāng)月某個工作日。如6#3表示當(dāng)月的第三個星期五(6表示星期五,#3表示當(dāng)前的第三個),而4#5表示當(dāng)月的第五個星期三,假設(shè)當(dāng)月沒有第五個星期三,忽略不觸發(fā);
- C:該字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是計劃所關(guān)聯(lián)的日期,如果日期沒有被關(guān)聯(lián),則相當(dāng)于日歷中所有日期。例如5C在日期字段中就相當(dāng)于日歷5日以后的第一天。1C在星期字段中相當(dāng)于星期日后的第一天。
示例:
表示式 | 說明 |
0 0 12 * * ? | 每天12點運行 |
0 15 10 ? * * | 每天10:15運行 |
0 15 10 * * ? | 每天10:15運行 |
0 15 10 * * ? * | 每天10:15運行 |
0 15 10 * * ? 2008 | 在2008年的每天10:15運行 |
0 * 14 * * ? | 每天14點到15點之間每分鐘運行一次,開始于14:00,結(jié)束于14:59。 |
0 0/5 14 * * ? | 每天14點到15點每5分鐘運行一次,開始于14:00,結(jié)束于14:55。 |
0 0/5 14,18 * * ? | 每天14點到15點每5分鐘運行一次,此外每天18點到19點每5鐘也運行一次。 |
0 0-5 14 * * ? | 每天14:00點到14:05,每分鐘運行一次。 |
0 10,44 14 ? 3 WED | 3月每周三的14:10分到14: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 2007-2009 | 在2007,2008,2009年每個月的最后一個星期五的10:15分運行。 |
0 15 10 ? * 6#3 | 每月第三個星期五的10:15分運行。 |
Quartz
Quartz 是一個完全由 Java 編寫的開源作業(yè)調(diào)度框架,它可以集成在幾乎任何Java應(yīng)用程序中進行作業(yè)調(diào)度。
Quartz 可以與 J2EE 與 J2SE 應(yīng)用程序相結(jié)合也可以單獨使用。
Quartz 允許程序開發(fā)人員根據(jù)時間的間隔來調(diào)度作業(yè)。
Quartz 實現(xiàn)了作業(yè)和觸發(fā)器的多對多的關(guān)系,還能把多個作業(yè)與不同的觸發(fā)器關(guān)聯(lián)。
Quartz的運行環(huán)境
Quartz 可以運行嵌入在另一個獨立式應(yīng)用程序。
Quartz 可以在應(yīng)用程序服務(wù)器(或 servlet 容器)內(nèi)被實例化,并且參與 XA 事務(wù)。
Quartz 可以作為一個獨立的程序運行(其自己的 Java 虛擬機內(nèi)),可以通過 RMI 使用。
Quartz 可以被實例化,作為獨立的項目集群(負載平衡和故障轉(zhuǎn)移功能),用于作業(yè)的執(zhí)行。
Job
代表一個工作,要執(zhí)行的具體內(nèi)容。此接口中只有一個方法,如下:
public class QuartzJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println(new Date()); } }
可以通過實現(xiàn)該接口來定義需要執(zhí)行的任務(wù)。
JobDetail
用于定義作業(yè)的實例,代表一個具體的可執(zhí)行的調(diào)度程序,Job 是這個可執(zhí)行程調(diào)度程序所要執(zhí)行的內(nèi)容,另外 JobDetail 還包含了這個任務(wù)調(diào)度的方案和策略。
Trigger
代表一個調(diào)度參數(shù)的配置,什么時候去調(diào)。
1> SimpleTrigger:在某個時間段內(nèi)實現(xiàn)定時任務(wù)的重復(fù)執(zhí)行。
參數(shù):startTime(開始時間)、endTime(結(jié)束時間)、repeatCount(重復(fù)數(shù)次)、repeatInterval(重復(fù)執(zhí)行間隔)
2> CronTrigger:基于日歷的概念執(zhí)行計劃,這個trigger是最常用的。
參數(shù):startTime(開始時間)、endTime(結(jié)束時間)、cronExpression(定時表達式)、timeZone(時區(qū),默認獲取jvm所在時區(qū))
Scheduler
代表一個調(diào)度容器,一個調(diào)度容器中可以注冊多個 JobDetail 和 Trigger。當(dāng) Trigger 與 JobDetail 組合,就可以被 Scheduler 容器調(diào)度了。
Calendar
是一些日歷特定時間的集合。一個Trigger可以和多個calendar關(guān)聯(lián),可以通過calendar在指定時間不執(zhí)行任務(wù)。
示例:服務(wù)啟動5秒后執(zhí)行,任務(wù)間隔2秒,服務(wù)啟動15秒后關(guān)閉
依賴
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency>
任務(wù)調(diào)度
@Component //SpringBoot服務(wù)啟動執(zhí)行 public class CronScheduler implements CommandLineRunner { @Override public void run(String... args) throws Exception { JobDetail build = JobBuilder.newJob(QuartzJob.class) .withIdentity("myJob", "group1") .build(); Date date = new Date(); long startTime = date.getTime() + 5000; long endTime = date.getTime() + 15000; CronTrigger c = TriggerBuilder.newTrigger() .startAt(new Date(startTime)) .endAt(new Date(endTime)) .withIdentity("CronTrigger1", "t1") .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) .build(); Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //設(shè)置調(diào)度的job和trigger scheduler.scheduleJob(build, c); //開啟調(diào)度 scheduler.start(); //暫停,可以重新啟動 //scheduler.standby(); //停止調(diào)度程序觸發(fā)觸發(fā)器,并清除與調(diào)度程序關(guān)聯(lián)的所有資源 //scheduler.shushutdown(); } }
具體執(zhí)行的任務(wù)
public class QuartzJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println(new Date()); } }
到此這篇關(guān)于springboot實現(xiàn)定時任務(wù)的四種方式小結(jié)的文章就介紹到這了,更多相關(guān)springboot 定時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Aop 如何獲取參數(shù)名參數(shù)值
這篇文章主要介紹了Spring Aop 如何獲取參數(shù)名參數(shù)值的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07利用openoffice+jodconverter-code-3.0-bate4實現(xiàn)ppt轉(zhuǎn)圖片
這篇文章主要為大家詳細介紹了利用openoffice+jodconverter-code-3.0-bate4實現(xiàn)ppt轉(zhuǎn)圖片,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07JAVA 16位ID生成工具類含16位不重復(fù)的隨機數(shù)數(shù)字+大小寫
這篇文章主要介紹了JAVA 16位ID生成工具類含16位不重復(fù)的隨機數(shù)數(shù)字+大小寫,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02springboot @ComponentScan注解原理解析
這篇文章主要介紹了springboot @ComponentScan注解原理解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02