Spring --- java定時器,Spring定時器和Quartz定時器
阿新 • • 發佈:2019-01-22
在現實生活中,會出現這樣的例子,比如燒水,一般燒了20分鐘後,水開了,會需要及時換水,再燒20分鐘,水又開了,繼續提醒,比如上班,每天早晨8點鐘的鬧鐘會及時提醒, 那麼在java程式中如何實現 這種 已經被設定了的定時的任務呢,在下面會依次介紹關於如何實現定時器來操作一些定時任務的業務需求。
當前java程式中 能夠實現定時的 主要有 三種 方式 ,分別是: java定時器 , spring定時器 , quartz定時器.下面依次講講他們的應用! <1>java定時器的應用。 其實java很早就有解決定時器任務的方法了,java提供了了類java.util.TimerTask類基於執行緒的方式來實現定時任務的操作,然後再提供java.util.Timer類來註冊呼叫,先建立一個類 RingTask 繼承 java.util.TimerTask,實現run方法,相關程式碼如下: package timer; import java.util.TimerTask; /** * 這是一個打鈴的程式,必須隔一段時間打一次 * @author sam * */ public class RingTask extends TimerTask{ public RingTask() { // TODO Auto-generated constructor stub } public RingTask(int s,int d) { // TODO Auto-generated constructor stub this.second = s; this.delay = d; } int second = 1; int delay = 1; public void setSecond(int second) { this.second = second; } public void setDelay(int delay) { this.delay = delay; } @Override public void run() { // TODO Auto-generated method stub System.out.println("我是打鈴程式!"+"我第一次打鈴延遲了"+delay+"秒!"); System.out.println("打鈴了!每過"+second+"秒一次"); } } 定義好後,下面需要註冊呼叫了,註冊呼叫的方法如下: public static void main(String[] args) { //以 java定時器的模式呼叫 Timer timer = new Timer(); timer.schedule( new RingTask(3,3), //需要註冊的定時類 3000, //最開始先延遲3秒的時間 3000); //每隔3秒的時間呼叫一次 } 一個簡單的java定時器就寫好了,方便而簡介,但是有不好的缺點: 如果需要實現每天早晨7點鐘的定時執行一次,且週末的時候早晨7點鐘不需要提醒,那這個可就不夠用了,並且如果需要伺服器一開啟就觸發這個定時器,則這種註冊呼叫的方法也是不行的 。
<2>Spring定時器的應用。
spring定時器是在spring框架中應用較成熟的一種方式,spring將定時任務的呼叫部分提到了配置檔案當中,使定時器的觸發條件變得更加靈活,spring定時器的實現,仍然需要 繼承 java.util.TimerTask,實現run方法 ,示例類上面已給出,呼叫的配置如下:
<!-- 定時器的配置 (spring定時器)-->
<!-- 要排程的bean配置 -->
<bean id="ringTask" class="timer.RingTask">
<!-- 給 屬性 second 賦值 為 3 -->
<property name="second" >
<value>3</value>
</property>
<!-- 給 屬性 delay 賦值 為 3 -->
<property name="delay" >
<value>3</value>
</property>
</bean>
<!--配置一個觸發器 配置觸發器的引數-->
<bean id="scheduleRingTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="delay" value="3000"></property> <!--第一次延遲3秒的時間-->
<property name="period" value="3000"></property> <!--每隔3秒的時間執行一次-->
<property name="timerTask" ref="ringTask"></property> <!--制定觸發的類-->
</bean>
<!-- 總排程,用於啟動定時器 -->
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref bean="scheduleRingTask"/>
</list>
</property>
</bean>
在呼叫方面是不是靈活些了,且能夠實現伺服器已啟動,就將定時器的執行納入的被監控的範圍,符合條件馬上觸發執行。但是還是存在缺點:
對於指定了具體的年月日時分秒而執行的任務還是不能解決。
<3>Quartz定時器
Quartz是基於Spring框架之上的更加強大的定時器,它不僅可以輕鬆的實現前面兩種定時器的功能,還實現了非常繁複的時間觸發執行的任務,Quartz有兩種方式來排程定時任務,一是使用Spring提供的 MethodInvokingJobDetailFactoryBean 代理類,Quartz通過該代理類直接排程任務類的某個函式;二是任務類繼承QuartzJobBean類或者實現org.quartz.Job介面,Quartz通過該父類或者介面進行排程。
先來看看實現前面個兩種定時器的功能,現在先來舉個例子,比如燒水,每1小時燒開一次,進行定時提醒,然後重新換水,再燒,。。。。
具體的燒水定時類程式碼如下:
package timer;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* 這是一個熱水的程式,必要要經過一段時間燒熱了,才提醒重新換水
* @author sam
* 這個類不管是繼承 QuartzJobBean還是實現org.quartz.Job都行
*/
public class HotWaterTask extends QuartzJobBean
/*implements Job*/{
//public void execute(JobExecutionContext arg0)
//throws JobExecutionException {
//// TODO Auto-generated method stub
//System.out.println("我是熱水程式,我第一燒水需要1小時");
//System.out.println("水現在燒開了,要及時換水哦!");
//}
@Override
protected void executeInternal(JobExecutionContext arg0)
throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println("我是熱水程式,
我第一燒水需要1小時 ");
System.out.println("水現在燒開了,要及時換水哦!");
}
}
類定義好了,下面需要配置進去,配置的程式碼如下:
<!-- 配置需要排程的任務類 -->
<bean id="hotWaterTask" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="timer.HotWaterTask"></property>
</bean>
<!-- 配置一個觸發器 -->
<bean id="hotWaterTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="hotWaterTask"></property>
<property name="startDelay" value="3600000"></property>
<property name="repeatInterval" value="
3600000"></property>
</bean>
<!-- 總排程,用於啟動定時器 -->
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers" >
<list>
<ref bean="hotWaterTrigger"/>
</list>
</property>
</bean>
看到了嗎,在這裡我們並沒有直接宣告一個
HotWaterTask Bean,而是聲明瞭一個JobDetailBean。這個是Quartz的特點。JobDetailBean是Quartz的org.quartz.JobDetail的子類,它要求通過jobClass屬性來設定一個Job物件。
好了,上面的任務已經實現了,下面看看 如何實現 具體的年月日時分秒執行的程式碼
Quartz在指定的時間執行 (很強大的代理定時執行機制)
(1) 定義上班鬧鐘定時類程式碼如下:
package timer;
/**
* 開始上班,這個程式要求每天(非週末)早晨八點需要啟動一次
* @author sam
*
*/
public class StartWorkJob {
public void startWork(){
System.out.println("我是上班程式,每天(非週末)早晨八點需要啟動一次");
System.out.println("上班了!~");
}
}
看到了嗎,這個類StartWorkJob 並沒有繼承任何類也沒有實現任何介面,且方法 startWork也是自己定義的,原有的業務程式碼不需要做任何更改。下面就要提到Quartz實現的一種機制,通過Spring提供的代理類(MethodInvokingJobDetailFactoryBean)來實現定時任務,這個類只需要提供它要代理的類以及要代理的方法,就能夠很好的就行定時監控了,強大吧,相關的程式碼如下:
<!-- 配置需要定時的bean類 -->
<bean id="startWorkJob" class="timer.StartWorkJob"></bean>
<!-- 配置任務的具體類和方法 -->
<bean id="startWorkTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!-- 要呼叫的bean -->
<property name="targetObject" ref="startWorkJob"></property>
<!-- 要呼叫的Method -->
<property name="targetMethod" value="startWork"></property>
<!-- 是否併發,false表示 如果發生錯誤也不影響下一次的呼叫 -->
<property name="concurrent" value="false"></property>
</bean>
<!-- 配置一個觸發器 -->
<bean id="startWorkTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="startWorkTask"></property>
<property name="cronExpression" value="0 * 13 * * ?"></property> <!--每天的下午1點的每分鐘的0秒都執行一次-->
</bean>
<!-- 總排程,用於啟動定時器 -->
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers" >
<list>
<ref bean="startWorkTrigger"/>
</list>
</property>
</bean>
好了一個指定了具體時間的定時觸發任務也已經實現了,下面來看看cronExpression
有哪些需要知道的配置資訊,資訊如下:
一個cron表示式有至少6個(也可能7個)有空格分隔的時間元素。從左到右:
1.秒2.分3.小時4.月份中的日期(1-31)5.月份(1-12或JAN-DEC)6.星期中的日期(1-7或SUN-SAT)7.年份(1970-2099)
每個元素都顯示的規定一個值(如6),一個區間(9-12),一個列表(9,11,13)或一個萬用字元(*)。因為4和6這兩個元素是互斥的,因此應該通過設定一個問號(?)來表明不想設定的那個欄位,“/”如果值組合就表示重複次數(10/6表示每10秒重複6次)。 示例如下: 0 0 12 * * ?---------------在每天中午12:00觸發
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:00至2:59之間每分鐘觸發一次
0 0/5 14 * * ?---------------每天在下午2:00至2:59之間每5分鐘觸發一次
0 0/5 14,18 * * ?---------------每天在下午2:00至2:59和6:00至6:59之間的每5分鐘觸發一次
0 0-5 14 * * ?---------------每天在下午2:00至2:05之間每分鐘觸發一次
0 10,44 14 ? 3 WED---------------每三月份的星期三在下午2:00和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, 2003, 2004 and 2005年的每個月的最後一個星期五的上午10:15觸發
0 15 10 ? * 6#3---------------在每個月的第三個星期五的上午10:15觸發
0 0 12 1/5 * ?---------------從每月的第一天起每過5天的中午12:00時觸發
當前java程式中 能夠實現定時的 主要有 三種 方式 ,分別是: java定時器 , spring定時器 , quartz定時器.下面依次講講他們的應用! <1>java定時器的應用。 其實java很早就有解決定時器任務的方法了,java提供了了類java.util.TimerTask類基於執行緒的方式來實現定時任務的操作,然後再提供java.util.Timer類來註冊呼叫,先建立一個類 RingTask 繼承 java.util.TimerTask,實現run方法,相關程式碼如下: package timer; import java.util.TimerTask; /** * 這是一個打鈴的程式,必須隔一段時間打一次 * @author sam * */ public class RingTask extends TimerTask{ public RingTask() { // TODO Auto-generated constructor stub } public RingTask(int s,int d) { // TODO Auto-generated constructor stub this.second = s; this.delay = d; } int second = 1; int delay = 1; public void setSecond(int second) { this.second = second; } public void setDelay(int delay) { this.delay = delay; } @Override public void run() { // TODO Auto-generated method stub System.out.println("我是打鈴程式!"+"我第一次打鈴延遲了"+delay+"秒!"); System.out.println("打鈴了!每過"+second+"秒一次"); } } 定義好後,下面需要註冊呼叫了,註冊呼叫的方法如下: public static void main(String[] args) { //以 java定時器的模式呼叫 Timer timer = new Timer(); timer.schedule( new RingTask(3,3), //需要註冊的定時類 3000, //最開始先延遲3秒的時間 3000); //每隔3秒的時間呼叫一次 } 一個簡單的java定時器就寫好了,方便而簡介,但是有不好的缺點: 如果需要實現每天早晨7點鐘的定時執行一次,且週末的時候早晨7點鐘不需要提醒,那這個可就不夠用了,並且如果需要伺服器一開啟就觸發這個定時器,則這種註冊呼叫的方法也是不行的
1.秒2.分3.小時4.月份中的日期(1-31)5.月份(1-12或JAN-DEC)6.星期中的日期(1-7或SUN-SAT)7.年份(1970-2099)
每個元素都顯示的規定一個值(如6),一個區間(9-12),一個列表(9,11,13)或一個萬用字元(*)。因為4和6這兩個元素是互斥的,因此應該通過設定一個問號(?)來表明不想設定的那個欄位,“/”如果值組合就表示重複次數(10/6表示每10秒重複6次)。 示例如下: 0 0 12 * * ?---------------在每天中午12:00觸發
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:00至2:59之間每分鐘觸發一次
0 0/5 14 * * ?---------------每天在下午2:00至2:59之間每5分鐘觸發一次
0 0/5 14,18 * * ?---------------每天在下午2:00至2:59和6:00至6:59之間的每5分鐘觸發一次
0 0-5 14 * * ?---------------每天在下午2:00至2:05之間每分鐘觸發一次
0 10,44 14 ? 3 WED---------------每三月份的星期三在下午2:00和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, 2003, 2004 and 2005年的每個月的最後一個星期五的上午10:15觸發
0 15 10 ? * 6#3---------------在每個月的第三個星期五的上午10:15觸發
0 0 12 1/5 * ?---------------從每月的第一天起每過5天的中午12:00時觸發