在作項目時有時候會有定時器任務的功能,好比某某時間應該作什麼,多少秒應該怎麼樣之類的。java
spring支持多種定時任務的實現。咱們來介紹下使用spring的定時器和使用quartz定時器spring
1.咱們使用spring-boot做爲基礎框架,其理念爲零配置文件,全部的配置都是基於註解和暴露bean的方式。數據庫
2.使用spring的定時器:併發
spring自帶支持定時器的任務實現。其可經過簡單配置來使用到簡單的定時任務。框架
@Component @Configurable @EnableScheduling public class ScheduledTasks{ @Scheduled(fixedRate = 1000 * 30) public void reportCurrentTime(){ System.out.println ("Scheduling Tasks Examples: The time is now " + dateFormat ().format (new Date ())); } //每1分鐘執行一次 @Scheduled(cron = "0 */1 * * * * ") public void reportCurrentByCron(){ System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ())); } private SimpleDateFormat dateFormat(){ return new SimpleDateFormat ("HH:mm:ss"); } }
沒了,沒錯,使用spring的定時任務就這麼簡單,其中有幾個比較重要的註解:maven
@EnableScheduling:標註啓動定時任務。ide
@Scheduled(fixedRate = 1000 * 30) 定義某個定時任務。函數
3.使用quartz實現定時任務。
Quartz設計者作了一個設計選擇來從調度分離開做業。Quartz中的觸發器用來告訴調度程序做業何時觸發。框架提供了一把觸發器類型,但兩個最經常使用的是SimpleTrigger和CronTrigger。SimpleTrigger爲須要簡單打火調度而設計。典型地,若是你須要在給定的時間和重複次數或者兩次打火之間等待的秒數打火一個做業,那麼SimpleTrigger適合你。另外一方面,若是你有許多複雜的做業調度,那麼或許須要CronTrigger。
CronTrigger是基於Calendar-like調度的。當你須要在除星期六和星期天外的天天上午10點半執行做業時,那麼應該使用CronTrigger。正如它的名字所暗示的那樣,CronTrigger是基於Unix克隆表達式的。spring-boot
使用quartz說使用的maven依賴。this
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>1.8.4</version> </dependency>
因爲咱們使用的是spring-boot框架,其目的是作到零配置文件,因此咱們不使用xml文件的配置文件來定義一個定時器,而是使用向spring容器暴露bean的方式。
向spring容器暴露所必須的bean
@Configuration public class SchedledConfiguration { // 配置中設定了 // ① targetMethod: 指定須要定時執行scheduleInfoAction中的simpleJobTest()方法 // ② concurrent:對於相同的JobDetail,當指定多個Trigger時, 極可能第一個job完成以前, // 第二個job就開始了。指定concurrent設爲false,多個job不會併發運行,第二個job將不會在第一個job完成以前開始。 // ③ cronExpression:0/10 * * * * ?表示每10秒執行一次,具體可參考附表。 // ④ triggers:經過再添加其餘的ref元素可在list中放置多個觸發器。 scheduleInfoAction中的simpleJobTest()方法 @Bean(name = "detailFactoryBean") public MethodInvokingJobDetailFactoryBean detailFactoryBean(ScheduledTasks scheduledTasks){ MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean (); bean.setTargetObject (scheduledTasks); bean.setTargetMethod ("reportCurrentByCron"); bean.setConcurrent (false); return bean; } @Bean(name = "cronTriggerBean") public CronTriggerBean cronTriggerBean(MethodInvokingJobDetailFactoryBean detailFactoryBean){ CronTriggerBean tigger = new CronTriggerBean (); tigger.setJobDetail (detailFactoryBean.getObject ()); try { tigger.setCronExpression ("0/5 * * * * ? ");//每5秒執行一次 } catch (ParseException e) { e.printStackTrace (); } return tigger; } @Bean public SchedulerFactoryBean schedulerFactory(CronTriggerBean[] cronTriggerBean){ SchedulerFactoryBean bean = new SchedulerFactoryBean (); System.err.println (cronTriggerBean[0]); bean.setTriggers (cronTriggerBean); return bean; }
}
MethodInvokingJobDetailFactoryBean:此工廠主要用來製做一個jobDetail,即製做一個任務。因爲咱們所作的定時任務根本上講其實就是執行一個方法。因此用這個工廠比較方便。
注意:其setTargetObject所設置的是一個對象而不是一個類。
CronTriggerBean:定義一個觸發器。
注意:setCronExpression:是一個表達式,若是此表達式不合規範,即會拋出異常。
SchedulerFactoryBean:主要的管理的工廠,這是最主要的一個bean。quartz經過這個工廠來進行對各觸發器的管理。
4.對quartz的封裝
由上面代碼能夠看出來,此處咱們設置的是一個固定的cronExpression,那麼,作爲項目中使用的話,咱們通常是須要其動態設置好比從數據庫中取出來。
其實作法也很簡單,咱們只須要定義一個Trigger來繼承CronTriggerBean。頂用其setCronExpression方法便可。
那麼另一個問題,若是咱們要定義兩個定時任務則會比較麻煩,須要先注入一個任務工廠,在注入一個觸發器。
爲了減小這樣的配置,咱們定義了一個抽象的超類來繼承CronTriggerBean。
具體代碼以下:
public abstract class BaseCronTrigger extends CronTriggerBean implements Serializable { private static final long serialVersionUID = 1L; public void init(){ // 獲得任務 JobDetail jobdetail = new JobDetail (this.getClass ().getSimpleName (),this.getMyTargetObject ().getClass ()); this.setJobDetail (jobdetail); this.setJobName (jobdetail.getName ()); this.setName (this.getClass ().getSimpleName ()); try { this.setCronExpression (this.getMyCronExpression ()); } catch (java.text.ParseException e) { e.printStackTrace (); } } public abstract String getMyCronExpression(); public abstract Job getMyTargetObject(); }
其init()方法,來爲這個觸發器綁定任務。其任務爲一個Job類型的,也就是說其執行的任務爲實現了Job接口的類,這個任務會有一個execute()方法,來執行任務題。
public class ScheduledTasks implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException{ System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date ())); } private SimpleDateFormat dateFormat(){ return new SimpleDateFormat ("HH:mm:ss"); } }
爲了給觸發器添加任務,咱們須要在子類中調用init()方法,因爲spring容器注入時是使用的空參的構造函數,因此咱們在此構造函數中調用init()方法。
@Component public class InitializingCronTrigger extends BaseCronTrigger implements Serializable { private static final long serialVersionUID = 1L; @Autowired private SchedulerFactoryBean schedulerFactoryBean; public InitializingCronTrigger() { init (); } @Override public String getMyCronExpression(){ return "0/5 * * * * ?"; } @Override public Job getMyTargetObject(){ return new ScheduledTasks (); } public void parse(){ try { schedulerFactoryBean.getObject ().pauseAll (); } catch (SchedulerException e) { e.printStackTrace (); } } }
此時咱們只須要在配置類中加入一個配置就能夠了。
@Bean public SchedulerFactoryBean schedulerFactory(CronTriggerBean[] cronTriggerBean){ SchedulerFactoryBean bean = new SchedulerFactoryBean (); System.err.println (cronTriggerBean[0]); bean.setTriggers (cronTriggerBean); return bean; }
4.介紹一個cronExpression表達式。
這一部分是摘抄的:
字段 容許值 容許的特殊字符 秒
0-59
, - * /
分
0-59
, - * /
小時
0-23
, - * /
日期
1-31
, - * / L W C
月份
1-12 或者 JAN-DEC
, - * /
星期
1-7 或者 SUN-SAT
, - * / L C #
年(可選)
留空, 1970-2099
, - * /
如上面的表達式所示:
「*」字符被用來指定全部的值。如:」*「在分鐘的字段域裏表示「每分鐘」。
「-」字符被用來指定一個範圍。如:「10-12」在小時域意味着「10點、11點、12點」。
「,」字符被用來指定另外的值。如:「MON,WED,FRI」在星期域裏表示」星期1、星期3、星期五」.
「?」字符只在日期域和星期域中使用。它被用來指定「非明確的值」。當你須要經過在這兩個域中的一個來指定一些東西的時候,它是有用的。看下面的例子你就會明白。
「L」字符指定在月或者星期中的某天(最後一天)。即「Last 」的縮寫。可是在星期和月中「L」表示不一樣的意思,如:在月子段中「L」指月份的最後一天-1月31日,2月28日,若是在星期字段中則簡單的表示爲「7」或者「SAT」。若是在星期字段中在某個value值得後面,則表示「某月的最後一個星期value」,如「6L」表示某月的最後一個星期五。
「W」字符只能用在月份字段中,該字段指定了離指定日期最近的那個星期日。
「#」字符只能用在星期字段,該字段指定了第幾個星期value在某月中每個元素均可以顯式地規定一個值(如6),一個區間(如9-12),一個列表(如9,11,13)或一個通配符(如*)。「月份中的日期」和「星期中的日期」這兩個元素是互斥的,所以應該經過設置一個問號(?)來代表你不想設置的那個字段。表7.1中顯示了一些cron表達式的例子和它們的意義:
表達式
意義 "0 0 12 * * ?"
天天中午12點觸發
"0 15 10 ? * *"
天天上午10:15觸發
"0 15 10 * * ?"
天天上午10:15觸發
"0 15 10 * * ? *"
天天上午10:15觸發
"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觸發
天天早上6點 0 6 * * *
每兩個小時 0 */2 * * *
晚上11點到早上8點之間每兩個小時,早上八點 0 23-7/2,8 * * *
每月的4號和每一個禮拜的禮拜一到禮拜三的早上11點 0 11 4 * 1-3
1月1日早上4點 0 4 1 1 *