這里主要是記錄下從Spring1.0到現(xiàn)在的5.0中定時(shí)器的配置方式桶癣,關(guān)于源碼,暫先不解釋。主要用作自己記錄用今野,如果有錯(cuò)誤的還請(qǐng)指出一起改正學(xué)習(xí)葡公,免得誤導(dǎo)別人,謝謝条霜。
Spring1中定時(shí)器的配置
直接看Spring1.1.1的文檔催什,里面都已經(jīng)給出來了各種配置方式,更高版本的也都包含了這些宰睡,但是覺得看1.1.1的更純粹一些蒲凶。
Spring1中對(duì)定時(shí)器的支持有兩種方式:
- jdk的Timer
- Quartz Scheduler
Quartz Scheduler
Quartz Scheduler使用Triggers,Jobs拆内,JobDetail來實(shí)現(xiàn)定時(shí)器功能旋圆。Spring提供了對(duì)Quartz的支持。
使用的大概步驟是:
- 定義JobDetail矛纹,也就是定義具體的任務(wù)臂聋。
- 定義trigger,就是定義觸發(fā)器或南,指定什么任務(wù)孩等,在什么時(shí)間執(zhí)行或者隔多久執(zhí)行。
- 定義SchedulerFactoryBean采够,來執(zhí)行任務(wù)肄方。
下面我們以一個(gè)例子來說明,是一個(gè)定時(shí)的去獲取信息和定時(shí)統(tǒng)計(jì)信息的示例蹬癌。
定義JobDetail
定義JobDetail有兩種方式权她,一種是使用JobDetailBean,一種是使用MethodInvokingJobDetailFactoryBean逝薪,后者可以指定要執(zhí)行的具體方法隅要。
使用JobDetailBean
CountUserJob:
package me.cxis.spring.scheduling.quartz;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* Created by cheng.xi on 2017-04-19 11:00.
* 定時(shí)的統(tǒng)計(jì)信息的JOb
* 比如這里是定時(shí)的統(tǒng)計(jì)系統(tǒng)中總的用戶數(shù),總的用戶數(shù)是我查詢到的數(shù)和我在xml指定的數(shù)的總和
*/
public class CountUserJob extends QuartzJobBean{
private int adminUser;
public void setAdminUser(int adminUser) {
this.adminUser = adminUser;
}
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
//執(zhí)行真正的統(tǒng)計(jì)任務(wù)
System.out.println("開始統(tǒng)計(jì)系統(tǒng)中人數(shù)");
System.out.println("統(tǒng)計(jì)完成董济,共有101人");
System.out.println("加上系統(tǒng)管理員之后共有" + (adminUser + 101) + "人");
}
}
xml中聲明一個(gè)job:
<!--定義一個(gè)JobDetailBean類型的Job步清,用來統(tǒng)計(jì)系統(tǒng)中總的人數(shù),adminUser指的是系統(tǒng)中預(yù)先留的管理員數(shù)目-->
<bean name="countUserJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>me.cxis.spring.scheduling.quartz.CountUserJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="adminUser">
<value>10</value>
</entry>
</map>
</property>
</bean>
使用MethodInvokingJobDetailFactoryBean
MethodInvokingJobDetailFactoryBean可以指定方法虏肾。直接看例子廓啊。
GetJob:
package me.cxis.spring.scheduling.quartz;
/**
* Created by cheng.xi on 2017-04-19 11:01.
* 定時(shí)的獲取信息的Job,定時(shí)從文件中獲取數(shù)據(jù)
*/
public class GetJob {
public void getSomethingFromFile(){
System.out.println("從文件中獲取數(shù)據(jù)封豪。谴轮。。吹埠。");
}
}
xml中配置:
<!--從文件中獲取信息的bean-->
<bean id="getJob" class="me.cxis.spring.scheduling.quartz.GetJob"/>
<!--定義一個(gè)MethodInvokingJobDetailFactoryBean第步,從文件中獲取數(shù)據(jù)的Job-->
<bean id="getJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject"><ref bean="getJob"/></property>
<property name="targetMethod"><value>getSomethingFromFile</value></property>
</bean>
定義Triggers
上面我們把JobDetail都定義好了疮装,也都配置好了,但是怎么去執(zhí)行雌续,多長(zhǎng)時(shí)間執(zhí)行一次都沒有說明斩个,這時(shí)候需要定義Triggers來描述任務(wù)什么時(shí)候執(zhí)行等。只需要在xml中配置就可以了驯杜。
Triggers也有兩種方式受啥,一種是SimpleTriggerBean,一種是CronTriggerBean鸽心。我們的例子中滚局,統(tǒng)計(jì)用戶數(shù)使用SimpleTriggerBean,從文件中獲取信息使用CronTriggerBean顽频。
<!--定義Triggers藤肢,統(tǒng)計(jì)用戶數(shù)-->
<bean id="countUserTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail">
<ref bean="countUserJob"/>
</property>
<!--第一次執(zhí)行之前需要等待的時(shí)間-->
<property name="startDelay">
<!--10秒-->
<value>10000</value>
</property>
<!--任務(wù)重復(fù)時(shí)間,每隔多少時(shí)間執(zhí)行一次-->
<property name="repeatInterval">
<value>20000</value>
</property>
</bean>
<!--定義Triggers糯景,定時(shí)從文件中獲取數(shù)據(jù)-->
<bean id="getJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="getJobDetail"/>
</property>
<property name="cronExpression">
<!--cron表達(dá)式嘁圈,這里是每隔兩分鐘執(zhí)行一次-->
<value>0 0/2 * * * ?</value>
</property>
</bean>
定義SchedulerFactoryBean
<!--定義SchedulerFactoryBean-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="countUserTrigger"/>
<ref local="getJobTrigger"/>
</list>
</property>
</bean>
測(cè)試
Main:
package me.cxis.spring.scheduling.quartz;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by cheng.xi on 2017-04-19 11:34.
*/
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:scheduling-quartz.xml");
}
}
JDK Timer
使用JDK Timer,也跟上面類似蟀淮,大概的步驟是:
- 創(chuàng)建一個(gè)TimerTask最住,里面是執(zhí)行任務(wù)的邏輯。
- 創(chuàng)建ScheduledTimerTask怠惶,就是什么時(shí)候或者隔多久執(zhí)行任務(wù)涨缚。
- 創(chuàng)建TimerFactoryBean,來執(zhí)行任務(wù)策治。
創(chuàng)建TimerTask
同樣脓魏,創(chuàng)建一個(gè)Task也有兩種方式,一種是繼承TimerTask通惫,另外一種是使用MethodInvokingTimerTaskFactoryBean茂翔,后者可以指定具體方法。
繼承TimerTask
CountUserTask:
package me.cxis.spring.scheduling.timer;
import java.util.TimerTask;
/**
* Created by cheng.xi on 2017-04-19 11:00.
* 定時(shí)的統(tǒng)計(jì)信息的 task
* 比如這里是定時(shí)的統(tǒng)計(jì)系統(tǒng)中總的用戶數(shù)履腋,總的用戶數(shù)是我查詢到的數(shù)和我在xml指定的數(shù)的總和
*/
public class CountUserTask extends TimerTask{
private int adminUser;
public void setAdminUser(int adminUser) {
this.adminUser = adminUser;
}
public void run() {
//執(zhí)行真正的統(tǒng)計(jì)任務(wù)
System.out.println("開始統(tǒng)計(jì)系統(tǒng)中人數(shù)");
System.out.println("統(tǒng)計(jì)完成檩电,共有101人");
System.out.println("加上系統(tǒng)管理員之后共有" + (adminUser + 101) + "人");
}
}
在xml中配置bean:
<!--統(tǒng)計(jì)用戶數(shù)的bean-->
<bean id="countUserTask" class="me.cxis.spring.scheduling.timer.CountUserTask">
<property name="adminUser">
<value>10</value>
</property>
</bean>
使用MethodInvokingTimerTaskFactoryBean
GetTask:
package me.cxis.spring.scheduling.timer;
/**
* Created by cheng.xi on 2017-04-19 11:01.
* 定時(shí)的獲取信息的task,定時(shí)從文件中獲取數(shù)據(jù)
*/
public class GetTask {
public void getSomethingFromFile(){
System.out.println("從文件中獲取數(shù)據(jù)府树。。料按。奄侠。");
}
}
xml中配置:
<!--從文件中獲取信息的bean-->
<bean id="getTaskBean" class="me.cxis.spring.scheduling.timer.GetTask"></bean>
<!--使用MethodInvokingTimerTaskFactoryBean-->
<bean id="getTask" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
<property name="targetObject"><ref bean="getTaskBean"/> </property>
<property name="targetMethod"><value>getSomethingFromFile</value></property>
</bean>
創(chuàng)建ScheduledTimerTask
定義ScheduledTimerTask來描述任務(wù)什么時(shí)候執(zhí)行等。只需要在xml中配置就可以了载矿。
使用Timer的方式垄潮,就這么一種配置烹卒,沒法使用cron的方式。
<!--創(chuàng)建統(tǒng)計(jì)用戶的ScheduledTimerTask弯洗,描述任務(wù)怎么運(yùn)行-->
<bean id="countUserScheduledTimerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="delay">
<value>10000</value>
</property>
<property name="period">
<value>20000</value>
</property>
<property name="timerTask">
<ref local="countUserTask"/>
</property>
</bean>
<!--創(chuàng)建從文件獲取信息的ScheduledTimerTask旅急,描述任務(wù)怎么運(yùn)行-->
<bean id="getScheduledTimerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="period">
<value>60000</value>
</property>
<property name="timerTask">
<ref local="getTask"/>
</property>
</bean>
創(chuàng)建TimerFactoryBean
任務(wù)執(zhí)行的配置:
<!--創(chuàng)建TimerFactoryBean,執(zhí)行任務(wù)-->
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="countUserScheduledTimerTask"/>
<ref local="getScheduledTimerTask"/>
</list>
</property>
</bean>
測(cè)試
package me.cxis.spring.scheduling.timer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by cheng.xi on 2017-04-19 11:34.
*/
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:scheduling-timer.xml");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面就是Spring1.x中關(guān)于定時(shí)器的配置方式牡整,配置清晰藐吮,易懂,但是任務(wù)多了之后逃贝,就會(huì)發(fā)現(xiàn)配置文件會(huì)迅速變得臃腫谣辞。
Spring2中定時(shí)器的配置
What's new in Spring 2.0?
- 增加對(duì)Executors的支持
上面就是AOP在2.0版本新增的特性,1.0的所有AOP配置方式在2.0中都支持沐扳,下面主要看看2.0中新增的一些方法泥从。
Spring2.0中新定義了一個(gè)TaskExecutor接口,增加了對(duì)線程池的支持沪摄,這個(gè)接口的功能跟JDK1.5中的Executor接口一樣躯嫉。那么2.0中線程池的增加,對(duì)定時(shí)器有什么影響呢杨拐?其實(shí)就是可以在定時(shí)任務(wù)執(zhí)行的時(shí)候祈餐,使用線程池來執(zhí)行任務(wù),我們不用關(guān)心其他的實(shí)現(xiàn)戏阅。
Spring2中配置示例
直接看示例昼弟,我們定時(shí),每隔5分鐘奕筐,每次都從20個(gè)文件中同時(shí)獲取數(shù)據(jù)舱痘。
首先寫實(shí)際執(zhí)行業(yè)務(wù)的類,
package me.cxis.spring.scheduling.executor;
/**
* Created by cheng.xi on 2017-04-19 14:51.
* 從文件中獲取數(shù)據(jù)的Task
*/
public class GetDataFromFileTask implements Runnable {
private int fileId;
public GetDataFromFileTask(int fileId){
this.fileId = fileId;
}
public void run() {
//真正執(zhí)行從文件中獲取數(shù)據(jù)的邏輯
System.out.println("從文件" + fileId + "中獲取數(shù)據(jù)");
}
}
然后是執(zhí)行任務(wù)的定時(shí)器:
package me.cxis.spring.scheduling.executor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.TimerTask;
/**
* Created by cheng.xi on 2017-04-19 14:54.
* 批量從文件中獲取數(shù)據(jù)的定時(shí)器
*/
public class GetDataFromFileScheduler extends TimerTask {
private ThreadPoolTaskExecutor executor;
public void setExecutor(ThreadPoolTaskExecutor executor) {
this.executor = executor;
}
public void run() {
System.out.println("CorePoolSize:" + taskExecutor.getCorePoolSize() + ";MaxPoolSize:" + taskExecutor.getMaxPoolSize());
//每次都會(huì)同時(shí)執(zhí)行從20個(gè)文件中獲取數(shù)據(jù)
for(int i = 0; i < 20;i++){
executor.execute(new GetDataFromFileTask(i));
}
}
}
接著是xml的配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--線程池taskExecutor-->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!--核心線程數(shù)-->
<property name="corePoolSize">
<value>5</value>
</property>
<!--最大線程數(shù)-->
<property name="maxPoolSize">
<value>10</value>
</property>
<!--隊(duì)列最大長(zhǎng)度-->
<property name="queueCapacity">
<value>40</value>
</property>
</bean>
<!--GetDataFromFileScheduler离赫,獲取數(shù)據(jù)的定時(shí)器-->
<bean id="getDataFromFileScheduler" class="me.cxis.spring.scheduling.executor.GetDataFromFileScheduler">
<property name="taskExecutor">
<ref local="taskExecutor"/>
</property>
</bean>
<!--創(chuàng)建統(tǒng)計(jì)用戶的ScheduledTimerTask芭逝,描述任務(wù)怎么運(yùn)行-->
<bean id="getDataFromFileTimerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="delay">
<value>10000</value>
</property>
<property name="period">
<value>20000</value>
</property>
<property name="timerTask">
<ref local="getDataFromFileScheduler"/>
</property>
</bean>
<!--創(chuàng)建TimerFactoryBean,執(zhí)行任務(wù)-->
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="getDataFromFileTimerTask"/>
</list>
</property>
</bean>
</beans>
測(cè)試類:
package me.cxis.spring.scheduling.executor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by cheng.xi on 2017-04-19 15:11.
*/
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:scheduling-executor.xml");
}
}
上面就是結(jié)合線程池的示例渊胸。也就是在執(zhí)行任務(wù)的時(shí)候多了線程池旬盯,基本的配置方式使用方法基本沒變。
Spring3中定時(shí)器的配置
- Spring3中TaskExecutor繼承了JDK的Executor翎猛。使用方面還是跟原來2.0一樣胖翰。
- Spring3中還引入了新的接口TaskScheduler,Trigger切厘,TriggerContext等萨咳。
- Spring3中還引入了task的命名空間
<task:scheduler/>
,<task:executor/>
疫稿,<task:scheduled-tasks/>
等培他。 - Spring3中還支持注解的方式
@Scheduled
鹃两,@Async
,使配置更加簡(jiǎn)化舀凛。使用注解的方式俊扳,需要在配置文件中先開啟注解支持<task:annotation-driven/>
注解方式的示例如下。
CountUserTask:
package me.cxis.spring.scheduling.annotation;
import org.springframework.scheduling.annotation.Scheduled;
/**
* Created by cheng.xi on 2017-04-19 11:00.
* 定時(shí)的統(tǒng)計(jì)信息的Task
*
*/
public class CountUserTask{
//@Scheduled(cron="*/5 * * * * MON-FRI") cron的方式
//下面是固定時(shí)間猛遍,每隔5秒執(zhí)行一次
@Scheduled(fixedRate = 5000)
public void countUser(){
//執(zhí)行真正的統(tǒng)計(jì)任務(wù)
System.out.println("開始統(tǒng)計(jì)系統(tǒng)中人數(shù)");
System.out.println("統(tǒng)計(jì)完成馋记,共有101人");
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!--開啟task的注解支持-->
<task:annotation-driven />
<!--執(zhí)行任務(wù)的bean-->
<bean id="countUserTask" class="me.cxis.spring.scheduling.annotation.CountUserTask"/>
</beans>
測(cè)試:
package me.cxis.spring.scheduling.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* Created by cheng.xi on 2017-04-19 16:08.
*/
public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:scheduling-annotation.xml");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以看到,使用注解的方式簡(jiǎn)化了很多很多螃壤。
Spring4和Spring5中定時(shí)器的配置
Spring4中增加了@EnableScheduling
注解來啟用對(duì)@Scheduled
注解的支持抗果。其他的基本沒有什么變化,使用方式還是跟以前一樣奸晴,現(xiàn)在使用注解更多冤馏。