日常系統開發中定時任務用的非常地普遍,比如我們可能想做個定時器去查詢某筆交易的狀態並進行彙總,又或者我們想在凌晨4點清楚資料庫的相關資料、又或者我們想在每月月底0點定時去開啟一個事務對當月、季度的資料做統計做成報表,灰常多呀!我也說不完啦!
有一點小編對於定時任務的理解,那就是:定時任務只是告訴系統在某個時刻執行某個任務,而至於該任務什麼時候執行完成,這不是定時任務要關心的範圍,定時任務只需要保證某個時刻發出呼叫某個任務的指令即可。
在Java領域定時任務實現的佼佼者就是quartz
了,quartz
框架在spring 3.0之前用的很廣泛,當然現在也是,quartz
支援資料持久化以及相應的叢集方式部署,spring 3.0 之後的spring版本自己實現了一套定時任務框架,我們可以把看做是quartz
的低配版本,它沒有提供對叢集的支援,不過其他功能已經灰常強大啦!
跟隨spring傳統,spring自己實現的定時任務框架spring-task同時支援xml和註解方式配置定時任務,當然小編還會講解我們如何自定義定時任務。翻開spring原始碼包我們可以發現,其實spring除了自家的spring-task,同時還提供了quartz
的整合,關於spring如何整合quartz
,請閱讀小編之前寫的一篇文章簡單說說Java 定時任務框架—Quartz
下面我將分別從xml、註解、自定義3個方面講解如何使用spring-task.
一、基於xml配置實現spring定時任務
定時任務元件分為三個:排程器、任務以及執行時間點。我們需要引入bean
、context
、core
三個spring包。
application.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!-- 建立一個排程器 -->
<task:scheduler id="schedualer"/>
<!-- 配置任務類的bean -->
<bean id="xmlTask" class="wokao666.club.spring_task.tasks.XmlTask"></bean>
<task:scheduled-tasks scheduler="schedualer">
<!-- 每2秒執行一次 -->
<task:scheduled ref="xmlTask" method="say" cron="0/2 * * * * ?"/>
</task:scheduled-tasks>
</beans>
複製程式碼
XmlTask.java
package wokao666.club.spring_task.tasks;
import java.text.SimpleDateFormat;
import wokao666.club.spring_task.util.DateFormatter;
/**
* 基於XML的spring定時任務
*/
public class XmlTask {
public void say() {
SimpleDateFormat format = DateFormatter.getDateFormatter();
System.err.println(format.format(System.currentTimeMillis()) + " I am spring xml-based task!");
}
}
複製程式碼
DateFormatter.java
package wokao666.club.spring_task.util;
import java.text.SimpleDateFormat;
/**
* 日期格式
* @author hjw
*
*/
public class DateFormatter {
private static volatile SimpleDateFormat formater = null;
private DateFormatter() {}
public static SimpleDateFormat getDateFormatter() {
if(null == formater) {
synchronized (DateFormatter.class) {
if(null == formater) {
formater = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
}
}
}
return formater;
}
}
複製程式碼
App.java
package wokao666.club.spring_task;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class App
{
private static ClassPathXmlApplicationContext ctx;
public static void main( String[] args ) {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
複製程式碼
程式輸出:
六月 10, 2018 10:47:11 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
資訊: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2328c243: startup date [Sun Jun 10 22:47:11 CST 2018]; root of context hierarchy
六月 10, 2018 10:47:11 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [applicationContext.xml]
六月 10, 2018 10:47:12 下午 org.springframework.scheduling.concurrent.ExecutorConfigurationSupport initialize
資訊: Initializing ExecutorService `schedualer`
2018-06-10 10:47:14 I am spring xml-based task!
2018-06-10 10:47:16 I am spring xml-based task!
2018-06-10 10:47:18 I am spring xml-based task!
複製程式碼
二、基於註解的spring-task
applicationContext.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!-- 開啟註解驅動 -->
<task:annotation-driven/>
<!-- bean 掃描 -->
<context:component-scan base-package="wokao666.club.spring_task.tasks"></context:component-scan>
</beans>
複製程式碼
AnnotationTask.java
package wokao666.club.spring_task.tasks;
import java.text.SimpleDateFormat;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import wokao666.club.spring_task.util.DateFormatter;
/**
* 測試基於註解的定時任務
* @author hjw
*/
@Service
public class AnnotationTask {
@Scheduled(cron="0/2 * * * * ?")
public void say() {
SimpleDateFormat format = DateFormatter.getDateFormatter();
System.err.println(format.format(System.currentTimeMillis()) + " good morning");
}
}
複製程式碼
App.java
、DateFormatter.java
和上面一樣,程式輸出如下:
六月 10, 2018 10:53:45 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
資訊: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2328c243: startup date [Sun Jun 10 22:53:45 CST 2018]; root of context hierarchy
六月 10, 2018 10:53:45 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [applicationContext.xml]
六月 10, 2018 10:53:46 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory registerBeanDefinition
資訊: Overriding bean definition for bean `org.springframework.context.annotation.internalScheduledAnnotationProcessor` with a different definition: replacing [Generic bean: class [org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.scheduling.annotation.SchedulingConfiguration; factoryMethodName=scheduledAnnotationProcessor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/scheduling/annotation/SchedulingConfiguration.class]]
六月 10, 2018 10:53:46 下午 org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor finishRegistration
資訊: No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2018-06-10 10:53:48 good morning
2018-06-10 10:53:50 good morning
2018-06-10 10:53:52 good morning
2018-06-10 10:53:54 good morning
複製程式碼
注意到上面輸出No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
這句話,因為你可以傳入一個java.util.Executor
執行緒池物件來實現對非同步任務的呼叫,如果你指定了executor
屬性,則spring會預設建立一個SimpleAsyncTaskExecutor
物件去執行,executor
屬性指定定時任務執行的執行緒池,強調執行,而scheduler
屬性表示任務排程的執行緒池,因為spring預設是單執行緒序列排程,如果想允許多執行緒併發排程,則需要配置scheduler
屬性,配置示例如下:
<!-- 排程執行緒池 -->
<task:scheduler id="scheduler" pool-size="10" />
<!-- 任務執行執行緒池 -->
<task:executor id="executor" pool-size="10" />
<task:annotation-driven executor="executor" scheduler="scheduler"/>
<context:component-scan base-package="wokao666.club.spring_task.tasks"></context:component-scan>
複製程式碼
(此處還需另行深入學習spring非同步任務相關知識原理,非同步執行使用@Async
標註方法)
三、自定義定時任務
實現自定義定時任務,或者說我們可以更改相關的cron
表示式,這看起來是很不可思議的,但確實可以做到,小編推薦一篇例文Spring @Scheduled定時任務動態修改cron引數
CustomSchedual.java
package wokao666.club.spring_task.tasks;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import wokao666.club.spring_task.util.DateFormatter;
/**
* 演示自定義定時器
*/
@EnableScheduling
@Component
public class CustomSchedual implements SchedulingConfigurer{
private static String cron = "0/2 * * * * ?";
private CustomSchedual() {
new Thread(()->{
try {
Thread.sleep(16000);
} catch (InterruptedException e) {}
cron = "0/10 * * * * ?";
System.err.println("change")
}).start();
}
public void configureTasks(ScheduledTaskRegistrar arg0) {
arg0.addTriggerTask(()->{
System.err.println(DateFormatter.getDateFormatter().format(System.currentTimeMillis()) + " good night!");
},(triggerContext)->{
CronTrigger trigger = new CronTrigger(cron);
return trigger.nextExecutionTime(triggerContext);
});
}
}
複製程式碼
程式輸出如下,可以看到,程式過來16秒之後,執行頻率從2秒/次變為10秒/次:
六月 10, 2018 11:49:13 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
資訊: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2328c243: startup date [Sun Jun 10 23:49:13 CST 2018]; root of context hierarchy
六月 10, 2018 11:49:13 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [applicationContext.xml]
六月 10, 2018 11:49:14 下午 org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor finishRegistration
資訊: No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2018-06-10 11:49:16 good night!
2018-06-10 11:49:18 good night!
2018-06-10 11:49:20 good night!
2018-06-10 11:49:22 good night!
2018-06-10 11:49:24 good night!
2018-06-10 11:49:26 good night!
2018-06-10 11:49:28 good night!
2018-06-10 11:49:30 good night!
change
2018-06-10 11:49:32 good night!
2018-06-10 11:49:40 good night!
2018-06-10 11:49:50 good night!
2018-06-10 11:50:00 good night!
2018-06-10 11:50:10 good night!
2018-06-10 11:50:20 good night!
複製程式碼
最後,單純會使用還不行,我們不僅要知其然,還要知其所以然,後期有時間多學學原理,其實底層無非就是java執行緒池的使用,加油,騷年們,晚安!