<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring 定時(shí)任務(wù)

          共 6376字,需瀏覽 13分鐘

           ·

          2020-10-02 22:29

          作者:techstay
          來(lái)源:SegmentFault 思否社區(qū)




          在程序中常常有定時(shí)任務(wù)的需求,例如每隔一周生成一次報(bào)表、每個(gè)月月末清空用戶積分等等。Spring也提供了相應(yīng)的支持,我們可以非常方便的按時(shí)執(zhí)行任務(wù)。




          項(xiàng)目準(zhǔn)備


          這里我使用Gradle來(lái)建立項(xiàng)目,然后在build.gradle中添加下面一行。springVersion的值是目前最新的Spring版本'4.3.7.RELEASE'。使用Maven的話也添加相應(yīng)的行。spring-context會(huì)自動(dòng)引入spring-core等幾個(gè)最基本的依賴。


          compile?group:?'org.springframework',?name:?'spring-context',?version:?springVersion



          定時(shí)任務(wù)屬于Spring的核心支持部分,所以我們不需要再添加其他的依賴了。所以定時(shí)任務(wù)功能既可以在命令行程序中使用,也可以在Java Web程序中使用。當(dāng)然后者可能使用的更廣泛一些(畢竟Web程序需要一直運(yùn)行的嘛)。


          這里我們定義兩個(gè)任務(wù),后面會(huì)讓它們可以定時(shí)執(zhí)行。


          public?interface?IService?{
          ????void?doService();
          }

          public?class?SimpleService?implements?IService?{
          ????@Override
          ????public?void?doService()?{
          ????????LocalTime?time?=?LocalTime.now();
          ????????System.out.println("This?is?a?simple?service:"?+?time);
          ????}
          }

          public?class?ExpensiveTaskService?implements?IService?{
          ????@Override
          ????public?void?doService()?{
          ????????try?{
          ????????????Thread.sleep(TimeUnit.SECONDS.toMillis(1));
          ????????????LocalTime?time?=?LocalTime.now();
          ????????????System.out.println("This?is?an?expensive?task:"?+?time);
          ????????}?catch?(InterruptedException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}
          }




          Spring的任務(wù)抽象


          TaskExecutor


          TaskExecutor接口是任務(wù)執(zhí)行接口,類似于java.util.concurrent.Executor ,該接口只有一個(gè)方法execute(Runnable task),用于執(zhí)行任務(wù)。


          Spring提供了一組TaskExecutor的實(shí)現(xiàn),詳細(xì)列表可以看這里34.2.1. TaskExecutor types。要使用它們也很簡(jiǎn)單,直接注冊(cè)為Spring Bean,然后注入到程序中即可使用。


          TaskScheduler


          TaskScheduler接口是定時(shí)器的抽象,它的源代碼如下??梢钥吹?,該接口包含了一組方法用于指定任務(wù)執(zhí)行的時(shí)間。


          public?interface?TaskScheduler?{

          ????ScheduledFuture?schedule(Runnable?task,?Trigger?trigger);

          ????ScheduledFuture?schedule(Runnable?task,?Date?startTime);

          ????ScheduledFuture?scheduleAtFixedRate(Runnable?task,?Date?startTime,?long?period);

          ????ScheduledFuture?scheduleAtFixedRate(Runnable?task,?long?period);

          ????ScheduledFuture?scheduleWithFixedDelay(Runnable?task,?Date?startTime,?long?delay);

          ????ScheduledFuture?scheduleWithFixedDelay(Runnable?task,?long?delay);

          }


          Spring提供了兩個(gè)實(shí)現(xiàn),一是TimerManagerTaskScheduler,會(huì)將任務(wù)代理到CommonJ TimerManager實(shí)例。第二個(gè)是ThreadPoolTaskScheduler,當(dāng)我們不需要管理線程的時(shí)候就可以使用該類。而且它還同時(shí)實(shí)現(xiàn)了TaskExecutor接口,所以一個(gè)ThreadPoolTaskScheduler實(shí)例即可同時(shí)用于執(zhí)行定時(shí)任務(wù)。


          Trigger


          在定時(shí)器接口的方法中我們可以發(fā)現(xiàn)一個(gè)方法接受Trigger接口, 而Trigger也是一個(gè)接口,抽象了觸發(fā)任務(wù)執(zhí)行的觸發(fā)器。


          Trigger接口有兩個(gè)實(shí)現(xiàn),先說(shuō)說(shuō)比較簡(jiǎn)單的一個(gè)PeriodicTrigger。它直接按照給定的時(shí)間間隔觸發(fā)任務(wù)執(zhí)行。更常用的一個(gè)觸發(fā)器是CronTrigger,它使用Cron表達(dá)式指定何時(shí)執(zhí)行任務(wù)。下面是Spring官方的一個(gè)例子。


          scheduler.schedule(task,?new?CronTrigger("0?15?9-17?*?*?MON-FRI"));



          關(guān)于Cron表達(dá)式的信息可以參考這篇博客QuartZ Cron表達(dá)式。另外還有一個(gè)可以在線生成Cron表達(dá)式的網(wǎng)站:CroMaker,不過(guò)好像需要XX才能訪問(wèn)。而且好像Spring不支持第二個(gè)星期一這樣的定時(shí)器設(shè)置,所以如果有這樣的需求,需要使用Quartz。




          配置任務(wù)


          任務(wù)配置既可以使用Java配置,也可以使用XML配置。不管使用哪種方法,首先需要將要執(zhí)行的方法所在的類配置為Spring Bean。例如下面就用XML配置注冊(cè)了兩個(gè)要執(zhí)行的任務(wù)。


          ????"simpleService"?class="yitian.study.service.SimpleService"/>
          ????"expensiveTaskService"
          ??????????class="yitian.study.service.ExpensiveTaskService"/>


          Java配置


          定時(shí)任務(wù)


          首先看看Java配置。我們需要在配置類上添加@EnableScheduling,如果需要異步的定時(shí)任務(wù),還需要添加@Async。


          @Configuration
          @EnableAsync
          @EnableScheduling
          public?class?TaskConfiguration?{
          }


          然后在要執(zhí)行的方法上添加@Scheduled注解。@Scheduled注解有幾個(gè)參數(shù),任務(wù)會(huì)在相應(yīng)參數(shù)的時(shí)間下執(zhí)行。cron參數(shù)指定Cron表達(dá)式;fixedDelay指定任務(wù)執(zhí)行的間隔,單位是毫秒;initialDelay指定當(dāng)程序啟動(dòng)后多長(zhǎng)時(shí)間開(kāi)始執(zhí)行第一次任務(wù),單位是毫秒;zone指定任務(wù)執(zhí)行時(shí)間所在的時(shí)區(qū)。下面的例子簡(jiǎn)單的指定了每隔一秒重復(fù)執(zhí)行一次任務(wù)。


          public?class?SimpleService?implements?IService?{
          ????@Scheduled(fixedDelay?=?1000)
          ????@Override
          ????public?void?doService()?{
          ????????LocalTime?time?=?LocalTime.now();
          ????????System.out.println("This?is?a?simple?service:"?+?time);
          ????}
          }


          異步任務(wù)


          然后是異步任務(wù),如果任務(wù)執(zhí)行時(shí)間比較長(zhǎng)的話,我們可以考慮使用異步的任務(wù)。當(dāng)調(diào)用異步任務(wù)的時(shí)候,異步方法直接返回,異步任務(wù)會(huì)交由相應(yīng)的任務(wù)執(zhí)行器來(lái)執(zhí)行。在Spring中標(biāo)記異步方法很簡(jiǎn)單,直接在方法上使用@Async注解。如果需要指定異步方法使用的執(zhí)行器,可以向注解傳遞執(zhí)行器的名稱。異步方法可以返回空值。


          @Async("otherExecutor")
          void?doSomething(String?s)?{
          ????//?this?will?be?executed?asynchronously?by?"otherExecutor"
          }


          但是如果異步方法想返回其他值的話,就必須使用Future。不過(guò)不僅是java.util.concurrent.Future,異步方法還可以返回Spring的org.springframework.util.concurrent.ListenableFuture和JDK8的java.util.concurrent.CompletableFuture類型。


          @Async
          Future?returnSomething(int?i)?{
          ????//?this?will?be?executed?asynchronously
          }


          異步方法不僅可以用于定時(shí)任務(wù)中,在Spring的其他地方也可以使用。例如Spring Data JPA可以使用@Async編寫異步的查詢方法。


          需要注意,異步方法沒(méi)有對(duì)應(yīng)的XML配置,如果我們想讓方法是異步的,只能使用注解。當(dāng)然也不是完全不行,不過(guò)就比較麻煩了,你需要使用AsyncExecutionInterceptor和AOP配合才能達(dá)到類似的效果。


          如果需要處理異步方法的異常,我們需要實(shí)現(xiàn)一個(gè)AsyncUncaughtExceptionHandler。下面的異步異常處理器簡(jiǎn)單的打印異常信息。


          public?class?MyAsyncUncaughtExceptionHandler?implements?AsyncUncaughtExceptionHandler?{
          ????@Override
          ????public?void?handleUncaughtException(Throwable?ex,?Method?method,?Object...?params)?{
          ????????ex.printStackTrace();
          ????}
          }


          然后通過(guò)實(shí)現(xiàn)AsyncConfigurer接口(Java配置方式)或者task:annotation-driven(XML配置方式)的exception-handler元素來(lái)配置。


          XML配置


          Spring提供了task命名空間,讓配置定時(shí)任務(wù)非常簡(jiǎn)單。


          定時(shí)器


          task:scheduler會(huì)注冊(cè)一個(gè)ThreadPoolTaskScheduler 定時(shí)器,它只有一個(gè)屬性線程池大小。默認(rèn)是1,我們需要根據(jù)任務(wù)的數(shù)量指定一個(gè)合適的大小。


          ????"threadPoolTaskScheduler"
          ????????????????????pool-size="10"/>


          執(zhí)行器


          task:executor會(huì)注冊(cè)一個(gè)ThreadPoolTaskExecutor執(zhí)行器,我們可以使用它的相關(guān)屬性來(lái)配置該執(zhí)行器。默認(rèn)情況下執(zhí)行隊(duì)列是無(wú)限的,可能會(huì)導(dǎo)致JVM使用完所有內(nèi)存。因此我們最好指定一個(gè)確定的數(shù)值。還有一個(gè)rejection-policy屬性,指定執(zhí)行器隊(duì)列滿時(shí)的執(zhí)行策略:默認(rèn)是AbortPolicy,直接拋出異常;如果當(dāng)系統(tǒng)忙時(shí)丟棄某些任務(wù)是可接受的,可以使用DiscardPolicy或DiscardOldestPolicy策略;當(dāng)系統(tǒng)負(fù)載較重時(shí)還可以使用CallerRunsPolicy,它不會(huì)將任務(wù)交給執(zhí)行器線程,而是讓調(diào)用者線程來(lái)執(zhí)行該任務(wù)。最后一個(gè)就是keep-alive屬性,也就是超出線程池?cái)?shù)量 線程完成任務(wù)之后的存活時(shí)間,單位是秒。


          ????"threadPoolTaskExecutor"
          ???????????????????pool-size="10"
          ???????????????????queue-capacity="10"/>


          執(zhí)行任務(wù)


          執(zhí)行任務(wù)很簡(jiǎn)單,使用指定要執(zhí)行的Bean和方法即可。

              

          ????????"simpleService"?method="doService"
          ????????????????????????cron="*/1?*?*?*?*?*"/>
          ????????"expensiveTaskService"?method="doService"
          ????????????????????????cron="*/2?*?*?*?*?*"/>
          ????


          要設(shè)置定時(shí)的話,只需要指定相應(yīng)的屬性即可。


          "myScheduler">
          ????"beanA"?method="methodA"?fixed-delay="5000"?initial-delay="1000"/>
          ????"beanB"?method="methodB"?fixed-rate="5000"/>
          ????"beanC"?method="methodC"?cron="*/5?*?*?*?*?MON-FRI"/>


          "myScheduler"?pool-size="10"/>




          Quartz集成


          Quartz是一個(gè)定時(shí)任務(wù)的庫(kù)。Spring也提供了它的支持。Quartz的使用方法請(qǐng)查閱相應(yīng)文檔。這里只簡(jiǎn)單介紹一下。


          Spring的Quartz集成在spring-context-support包中,它還需要Spring事務(wù)的支持。因此我們需要下面這樣的依賴聲明。


          ?compile?group:?'org.springframework',?name:?'spring-tx',?version:?springVersion
          ????compile?group:?'org.springframework',?name:?'spring-context-support',?version:?springVersion
          ????compile?group:?'org.quartz-scheduler',?name:?'quartz',?version:?'2.2.3'



          定義任務(wù)


          Quartz的任務(wù)需要繼承Quartz的Job接口。所以一個(gè)典型的任務(wù)可以寫成這樣。


          public?class?QuartzService?implements?IService,?Job?{
          ????@Override
          ????public?void?doService()?{
          ????????System.out.println("This?is?a?quartz?service");
          ????}

          ????@Override
          ????public?void?execute(JobExecutionContext?context)?throws?JobExecutionException?{
          ????????System.out.println("Do?something?in?execute?method?of?quartz");
          ????}
          }


          JobDetailFactoryBean


          JobDetailFactoryBean用來(lái)定義實(shí)現(xiàn)了Job接口的任務(wù)。如果需要添加更多信息,可以使用jobDataAsMap屬性設(shè)置。


          "jobDetail"
          ??????class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
          ????"jobClass"?value="yitian.study.service.QuartzService"/>
          ????"jobDataAsMap">
          ????????
          ????????????"timeout"?value="10"/>
          ????????

          ????


          MethodInvokingJobDetailFactoryBean


          如果任務(wù)沒(méi)有實(shí)現(xiàn)Job接口,也可以執(zhí)行,這時(shí)候需要使用MethodInvokingJobDetailFactoryBean。如果存在任務(wù)對(duì)象,使用targetObject屬性,如果有任務(wù)類,使用targetClass屬性。


          "methodJobDetail"?class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
          ????"targetObject"?ref="quartzService"/>
          ????"targetMethod"?value="doService"/>
          ????"concurrent"?value="true"/>


          觸發(fā)器


          有了任務(wù),就可以定義觸發(fā)器了。觸發(fā)器有兩個(gè):SimpleTriggerFactoryBean,以指定的間隔重復(fù)執(zhí)行任務(wù);CronTriggerFactoryBean,以給定的Cron表達(dá)式執(zhí)行任務(wù)。Quartz的Cron表達(dá)式比Spring 的強(qiáng)大,它支持第幾個(gè)星期幾這樣的Cron表達(dá)式。


          "simpleTrigger"?class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
          ????"jobDetail"?ref="jobDetail"/>
          ????"startDelay"?value="0"/>
          ????"repeatInterval"?value="1000"/>

          "cronTrigger"?class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
          ????"jobDetail"?ref="methodJobDetail"/>
          ????"cronExpression"?value="*/2?*?*?*?*??"/>


          執(zhí)行任務(wù)


          有了觸發(fā)器,我們就可以執(zhí)行任務(wù)了。注冊(cè)一個(gè)SchedulerFactoryBean,然后將觸發(fā)器的Bean引用傳入即可。


          "quartzScheduler"?class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
          ????"triggers">
          ????????
          ????????????"cronTrigger"/>
          ????????????"simpleTrigger"/>
          ????????

          ????




          本文參考自:

          • Spring官方文檔 34. Task Execution and Scheduling,詳細(xì)網(wǎng)址:
          • https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/#scheduling





          點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開(kāi)更多互動(dòng)和交流。



          -?END -

          瀏覽 33
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产强奸视频 | 91久久爽久久爽爽久久片 | 亚洲内射学生妹 | 69久久久久久久 | 91三级在线观看 |