在spring boot中,支持多種定時執行模式(cron, fixRate, fixDelay),在Application或者其他Autoconfig上增加@EnableScheduling注解開啟。
然后在指定方法增加@Scheduled注解,如下:
@Scheduled(cron="0 0 0/1 * * ?") public void updateTime() { current_log_time_appendix = sdf.format(new Date()); logger.info("日志文件切換, 切換后為:" + current_log_time_appendix); }
需要注意的是,如果在多個函數上使用了@Scheduled,那么一定是一個執行完畢,才能排下一個。這往往不是我們想要的效果。此時需要在Scheduling配置類為schedule返回一個預定的線程池,如下:
@Configuration @EnableScheduling public class SchedulingConfiguration { @Bean(destroyMethod = "shutdown") public Executor taskScheduler() { return Executors.newScheduledThreadPool(10); } }
完成之后,多個@Scheduled可以並發執行了,最高並發度是3,但是同一個@Schedule不會並發執行。
除了cron表達式和quartz、es-job以及linux cron等一樣靈活外, spring容器托管的定時任務方法能夠利用現成的依賴注入體系,例如:
@Scheduled(cron="0 * * * * *") // 表達式也可以通過@Value注入,例如@Scheduled(cron = "${cron_expression}") public void ss() { XXXParameter<BaseParameter> param = new TaLiquidateParameter<>(); param.setTenantCode("*"); param.setLongTaCode("F6"); param.setDatabaseNo("1"); BaseParameter data = new BaseParameter(); data.setLiqBatchNo("1"); data.setIoFlow("EXPORTCONDATA"); param.setData(data); interfaceDataPrepare(param ); // 當前bean的方法,會依賴其它bean }
其它的方式就必須通過ApplicationContext取主動lookup的方式獲取了。但是它的缺點是不支持集群,此時仍然需要其它控制機制,所以用來做測試或者其它不需要集群的場景是不錯的。
各種cron表達式備忘如下:
0 * * * * *:每分鍾(當秒為0的時候)0 0 * * * *:每小時(當秒和分都為0的時候)*/10 * * * * *:每10秒0 5/15 * * * *:每小時的5分、20分、35分、50分0 0 9,13 * * *:每天的9點和13點0 0 8-10 * * *:每天的8點、9點、10點0 0/30 8-10 * * *:每天的8點、8點半、9點、9點半、10點0 0 9-17 * * MON-FRI:每周一到周五的9點、10點…直到17點(含)0 0 0 25 12 ?:每年12約25日聖誕節的0點0分0秒(午夜)0 30 10 * * ? 2016:2016年每天的10點半
其中的?在用法上其實和*是相同的。但是*語義上表示全匹配,而?並不代表全匹配,而是不關心。比如對於0 0 0 5 8 ? 2016來說,2016年8月5日是周五,?表示我不關心它是周幾。而0 0 0 5 8 * 2016中的*表示周一也行,周二也行……語義上和2016年8月5日沖突了,你說誰優先生效呢。
不記得也沒關系,記住Cron Maker也可以,它可以在線生成cron表達式。
坑
同時運行
同一個task,如果前一個還沒跑完后面一個就不會觸發,這沒有問題。但是不同的task也不能同時運行就不太合理了。不過其實是scheduler的默認線程數為1的緣故。
解決方法1:如下配置pool-size,但這樣會導致同一個task前一個還沒跑完后面又被觸發的問題。
| <task:scheduler id="scheduler" pool-size="2" /> |
解決方法2:讓任務分別運行在不同的scheduler里。例如:
<task:scheduler id="myScheduler1"/> <task:scheduler id="myScheduler2"/> <task:scheduled-tasks scheduler="myScheduler1"> <task:scheduled ref="doSomethingTask" method="doSomething" cron="${0 * * * * *}"/> </task:scheduled-tasks> <task:scheduled-tasks scheduler="myScheduler2"> <task:scheduled ref="doOtherThingTask" method="doOtherThing" cron="${0 * * * * *}"/> </task:scheduled-tasks>
