文档章节

Spring task定时任务

z
 zh151832
发布于 2016/09/13 10:16
字数 4030
阅读 53
收藏 6

 

在工作中有用到spring task作为定时任务的处理,spring通过接口TaskExecutorTaskScheduler这两个接口的方式为异步定时任务提供了一种抽象。这就意味着spring容许你使用其他的定时任务框架,当然spring自身也提供了一种定时任务的实现:spring task。spring task支持线程池,可以高效处理许多不同的定时任务。同时,spring还支持使用Java自带的Timer定时器和Quartz定时框架。限于篇幅,这里将只介绍spring task的使用。

其实,官方文档已经介绍地足够详细,只不过都是英文版,所以为了更好地理解并使用spring task,首先会对spring task的实现原理做一个简单的介绍,然后通过实际代码演示spring task是如何使用的。这里会涉及到一个很重要的知识点:cron表达式。

TaskExecutor和TaskScheduler

TaskExecutor是spring task的第一个抽象,它很自然让人联想到jdk中concurrent包下的Executor,实际上TaskExecutor就是为区别于Executor才引入的,而引入TaskExecutor的目的就是为定时任务的执行提供线程池的支持,那么,问题来了,为什么spring不直接使用jdk自带的Executor呢?TaskExecutor源码如下?

public interface TaskExecutor extends Executor {
    void execute(Runnable var1);
}
  • 1
  • 2
  • 3

那么,答案很显然,TaskExecutor提供的线程池支持也是基于jdk自带的Executor的。用法于Executor没有什么不同。

TaskScheduler是spring task的第二个抽象,那么从字面的意义看,TaskScheduler就是为了提供定时任务的支持咯。TaskScheduler需要传入一个Runnable的任务做为参数,并指定需要周期执行的时间或者触发器,这样Runnable任务就可以周期性执行了。传入时间很好理解,有意思的是传入一个触发器(Trigger)的情况,因为这里需要使用cron表达式去触发一个定时任务,所以有必要先了解下cron表达式的使用。

在spring 4.x中已经不支持7个参数的cronin表达式了,要求必须是6个参数(具体哪个参数后面会说)。cron表达式的格式如下:

{秒} {分} {时} {日期(具体哪天)} {月} {星期}
  • 1
  • 秒:必填项,允许的值范围是0-59,支持的特殊符号包括 
    ,
     - * /,表示特定的某一秒才会触发任务,-表示一段时间内会触发任务,*表示每一秒都会触发,/表示从哪一个时刻开始,每隔多长时间触发一次任务。
  • 分:必填项,允许的值范围是0-59,支持的特殊符号和秒一样,含义类推
  • 时:必填项,允许的值范围是0-23,支持的特殊符号和秒一样,含义类推
  • 日期:必填项,允许的值范围是1-31,支持的特殊符号相比秒多了?,表示与{星期}互斥,即意味着若明确指定{星期}触发,则表示{日期}无意义,以免引起冲突和混乱。
  • 月:必填项,允许的值范围是1-12(JAN-DEC),支持的特殊符号与秒一样,含义类推
  • 星期:必填项,允许值范围是1~7 (SUN-SAT),1代表星期天(一星期的第一天),以此类推,7代表星期六,支持的符号相比秒多了?,表达的含义是与{日期}互斥,即意味着若明确指定{日期}触发,则表示{星期}无意义。

比如下面这个cron表达式:

// 表达的含义是:每半分钟触发一次任务
30 * * * * ?
  • 1
  • 2

spring提供了一个CronTrigger,通过传入一个Runnable任务和CronTrigger,就可以使用cron表达式去指定定时任务了,是不是非常方面。实际上,在工程实践上,cron表达式也是使用很多的。实际上,是执行了下面的代码:

scheduler.schedule(task, new CronTrigger("30 * * * * ?"));
  • 1

TaskScheduler抽象的好处是让需要执行定时任务的代码不需要指定特定的定时框架(比如Timer和Quartz)。TaskScheduler的更简单的实现是ThreadPoolTaskScheduler,它实际上代理一个jdk中的SchedulingTaskExecutor,并且也实现了TaskExecutor接口,所以需要经常执行定时任务的场景可以使用这个实现(Spring推荐)。我们再来看一下TaskExecutor和TaskScheduler的类继承关系:

spring-task

通常而言,使用spring task实现定时任务有两种方式:注解和xml配置文件。这里使用xml配置文件的方式加以说明。

实战

创建Maven工程,pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rhwayfun</groupId>
    <artifactId>sring-task-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

开发需要执行定时任务的方法:

package com.rhwayfun.task;

import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * @author ZhongCB
 * @date 2016年09月10日 14:30
 * @description
 */
@Component
public class App {

    public void execute1(){
        System.out.printf("Task: %s, Current time: %s\n", 1, LocalDateTime.now());
    }

    public void execute2(){
        System.out.printf("Task: %s, Current time: %s\n", 2, LocalDateTime.now());
    }

    public void execute3(){
        System.out.printf("Task: %s, Current time: %s\n", 3, LocalDateTime.now());
    }

    public void execute4(){
        System.out.printf("Task: %s, Current time: %s\n", 4, LocalDateTime.now());
    }

    public void execute5(){
        System.out.printf("Task: %s, Current time: %s\n", 5, LocalDateTime.now());
    }

    public void execute6(){
        System.out.printf("Task: %s, Current time: %s\n", 6, LocalDateTime.now());
    }

    public void execute7(){
        System.out.printf("Task: %s, Current time: %s\n", 7, LocalDateTime.now());
    }

    public void execute8(){
        System.out.printf("Task: %s, Current time: %s\n", 8, LocalDateTime.now());
    }

    public void execute9(){
        System.out.printf("Task: %s, Current time: %s\n", 9, LocalDateTime.now());
    }

    public void execute10(){
        System.out.printf("Task: %s, Current time: %s\n", 10, LocalDateTime.now());
    }

    public void execute11(){
        System.out.printf("Task: %s, Current time: %s\n", 11, LocalDateTime.now());
    }

}

spring配置文件如下:

<?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:context="http://www.springframework.org/schema/context"
       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.1.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd">

    <!-- 配置注解扫描 -->
    <context:component-scan base-package="com.rhwayfun.task"/>

    <task:scheduler id="taskScheduler" pool-size="100" />

    <task:scheduled-tasks scheduler="taskScheduler">
        <!-- 每半分钟触发任务 -->
        <task:scheduled ref="app" method="execute1" cron="30 * * * * ?"/>
        <!-- 每小时的10分30秒触发任务 -->
        <task:scheduled ref="app" method="execute2" cron="30 10 * * * ?"/>
        <!-- 每天1点10分30秒触发任务 -->
        <task:scheduled ref="app" method="execute3" cron="30 10 1 * * ?"/>
        <!-- 每月20号的1点10分30秒触发任务 -->
        <task:scheduled ref="app" method="execute4" cron="30 10 1 20 * ?"/>
        <!-- 每年10月20号的1点10分30秒触发任务 -->
        <task:scheduled ref="app" method="execute5" cron="30 10 1 20 10 ?"/>
        <!-- 每15秒、30秒、45秒时触发任务 -->
        <task:scheduled ref="app" method="execute6" cron="15,30,45 * * * * ?"/>
        <!-- 15秒到45秒每隔1秒触发任务 -->
        <task:scheduled ref="app" method="execute7" cron="15-45 * * * * ?"/>
        <!-- 每分钟的每15秒时任务任务,每隔5秒触发一次 -->
        <task:scheduled ref="app" method="execute8" cron="15/5 * * * * ?"/>
        <!-- 每分钟的15到30秒之间开始触发,每隔5秒触发一次 -->
        <task:scheduled ref="app" method="execute9" cron="15-30/5 * * * * ?"/>
        <!-- 每小时的0分0秒开始触发,每隔3分钟触发一次 -->
        <task:scheduled ref="app" method="execute10" cron="0 0/3 * * * ?"/>
        <!-- 星期一到星期五的10点15分0秒触发任务 -->
        <task:scheduled ref="app" method="execute11" cron="0 15 10 ? * MON-FRI"/>
    </task:scheduled-tasks>

</beans>

编写测试代码:

package com.rhwayfun.task;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author ZhongCB
 * @date 2016年09月10日 14:55
 * @description
 */
public class AppTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/app-context-task.xml");
    }
}

运行测试代码,控制台会定时输出每个定时任务的日志信息,说明测试通过。

 

1.cron表达式格式:

{秒} {分} {时} {日} {月} {周} {年(可选)}

2.cron各选项的取值范围及解释:
{秒}:取值范围(0-59),不允许为空值,若值不合法,调度器将抛出SchedulerException异常
{分}:取值范围(0-59),不允许为空值,若值不合法,调度器将抛出SchedulerException异常
{时}:取值范围(0-23),不允许为空值,若值不合法,调度器将抛出SchedulerException异常

"*" 代表每隔1分/秒/时触发;
"," 代表在指定的分/秒/时触发,比如"10,20,40"代表10分/秒/时、20分/秒/时和40分/秒/时时触发任务
"-" 代表在指定的范围内触发,比如"5-30"代表从5分/秒/时开始触发到30分/秒/时结束触 发,每隔1分/秒/时触发
"/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"0"),后面的值代表偏移量,比如"0/25"或者"*/25"代表从0分/秒/时开始,每隔25分/秒/时触发1次,即0分/秒/时触发1次,第25分/秒/时触发1次,第50分/秒/时触发1次;"5/25"代表5分/秒/时触发1次,30分/秒/时触发1次,55分/秒/时触发1次;"10-45/20"代表在[10,45]内步进20分/秒/时命中的时间点触发,即10分/秒/时触发1次,30分/秒/时触发1次

{日}:取值范围(1-31),不允许为空值,若值不合法,调度器将抛出SchedulerException异常

"*" 代表每天触发;
"?" 与{周}互斥,即意味着若明确指定{周}触发,则表示{日}无意义,以免引起 冲突和混乱;
"," 代表在指定的日期触发,比如"1,10,20"代表1号、10号和20号这3天触发;
"-" 代表在指定的日期范围内触发,比如"10-15"代表从10号开始触发到15号结束触发,每隔1天触发
"/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"1"),后面的值代表偏移量,比如"1/5"或者"*/5"代表从1号开始触发,每隔5天触发1次;"10/5"代表从10号开始触发,以后每隔5天触发一次;"1-10/2"表达式意味着在[1,10]范围内,每隔2天触发,即1号,3号,5号,7号,9号触发
"L" 如果{日}占位符如果是"L",即意味着当月的最后一天触发
"W "意味着在本月内离当天最近的工作日触发,所谓最近工作日,即当天到工作日的前后最短距离,如果当天即为工作日,则距离为0;所谓本月内的说法,就是不能跨月取到最近工作日,即使前/后月份的最后一天/第一天确实满足最近工作日;因此,"LW"则意味着本月的最后一个工作日触发,"W"强烈依赖{月}
"C" 根据日历触发,由于使用较少,暂时不做解释

{月}:取值范围(1-12或JAN-DEC),不允许为空值,若值不合法,调度器将抛出SchedulerException异常
{周}:取值范围(1-7或SUN-SAT),1表示星期天,2表示星期一, 依次类推,不允许为空值,若值不合法,调度器将抛出SchedulerException异常

"*" 代表每星期都触发;
"?" 与{日}互斥,即意味着若明确指定{日}触发,则表示{周}无意义,以免引起冲突和混乱
"," 代表在指定的星期约定触发,比如"1,3,5"代表星期天、星期二和星期四触发
"-" 代表在指定的星期范围内触发,比如"2-4"代表从星期一开始触发到星期三结束触发,每隔1天触发
"/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"1"),后面的值代表偏移量,比如"1/3"或者"*/3"代表从星期天开始触发,每隔3天触发1次;"1-5/2"表达式意味着在[1,5]范围内,每隔2天触发,即星期天、星期二、星期四触发
"L" 如果{周}占位符如果是"L",即意味着星期的的最后一天触发,即星期六触发,L= 7或者 L = SAT,因此,"5L"意味着一个月的最后一个星期四触发
"#" 用来指定具体的周数,"#"前面代表星期,"#"后面代表本月第几周,比如"2#2"表示本月第二周的星期一,"5#3"表示本月第三周的星期四,因此,"5L"这种形式只不过是"#"的特殊形式而已
"C" 根据日历触发,由于使用较少,暂时不做解释

{年}:取值范围(1970-2099),允许为空值,(非空时)若值不合法,调度器将抛出SchedulerException异常

注:
①.秒、分、时、月、年这5项的占位符以及占位符的使用规则是一致的(只是单位上的区别),日、周中标红的两条需要重点比较!

②.“?”和“L”字符仅被用于{日}和{周}两个子表达式,表示不指定值 。对于“?”的应用,当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”;在{日}表达式中,“L”表示一个月的最后一天,在{周}自表达式中,“L”表示一个星期的最后一天。如果在“L”前有具体的内容,它就具有其他的含义了。例如:“6L”表示这个月的倒数第6天,“FRIL”表示这个月的最一个星期五 。

③.在使用“L”参数时,不要指定列表或范围,因为这会导致问题

常用案例:

"0 0 10,14,16 * * ?" 每天上午10点,下午2点,4点 
"0 0/30 9-17 * * ?" 朝九晚五工作时间内每半小时 
"0 0 12 ? * WED" 表示每个星期三中午12点 
"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触发
"30 * * * * ?" 每半分钟触发任务
"30 10 * * * ?" 每小时的10分30秒触发任务
"30 10 1 * * ?" 每天1点10分30秒触发任务
"30 10 1 20 * ?" 每月20号1点10分30秒触发任务
"30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务
"30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务
"30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务
"30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务
"15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务
"15-45 * * * * ?" 15到45秒内,每秒都触发任务
"15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次
"15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
"0 0/3 * * * ?" 每小时的第0分0秒开始,每三分钟触发一次
"0 15 10 ? * MON-FRI" 星期一到星期五的10点15分0秒触发任务
"0 15 10 L * ?" 每个月最后一天的10点15分0秒触发任务
"0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务
"0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务
"0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务
"0 0 0 * * ?" 每天晚上12点触发任务


 

© 著作权归作者所有

共有 人打赏支持
z
粉丝 7
博文 106
码字总数 46061
作品 0
攀枝花
私信 提问
Spring task配置,及解决加载两次的方法

关于 启动Task任务同时加载两次的解决方法: 将spring MVC部分的定义另外建立一个文件,同时把Task配置放在此处,然后在web.xml文件中的处加载 <servlet><servlet-name>SpringMVC</servlet-...

夏碌冬藏
2014/08/04
0
2
分布式定时任务框架---Uncode Schedule

分布式定时任务框架---Uncode Schedule rabbitGYK 关注 2016.11.27 20:36* 字数 1446 阅读 7141评论 5喜欢 36赞赏 1 博客原文 作为一个支付公司的项目组,经常会有很多对账功能(签约对账、支...

晨猫
2018/11/02
0
0
Spring task的异步定时任务

背景 有时候,我们需要一些定时任务,帮助我们完成一些日常的操作,从而让我们从琐碎的工作解脱出来,这里使用Spring Task定时异步任务来处理。 步骤 配置命名空间 配置Spring Task Note:这里...

亚林瓜子
2018/06/12
0
0
实现Spring中的任务调度及异步执行

首先要端正一下本人的态度,开发了很长时间的Java Web项目,寄托于Spring MVC的架构,多数时间都是在处理业务逻辑问题,所以我个人单纯地认为Web开发,多线程的应用场景应该不多,能不用尽量...

chace0120
2014/08/01
0
1
关于Spring定时任务(定时器)用法

Spring定时任务的几种实现 博客分类: spring框架 quartzspringspring-task定时任务注解 Spring定时任务的几种实现 近日项目开发中需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前...

脸大的都是胖纸
2015/03/20
0
1

没有更多内容

加载失败,请刷新页面

加载更多

Windows同步对象Event和Linux的条件变量

最近在看一些同步对象模拟的东东,特别对在Windows下如何模拟条件变量折腾了很久。 1 Windows同步对象Event 微软有一个很有意思的同步对象,某种程度上和Linux的条件变量很相似。但秉承微软一...

shzwork
10分钟前
0
0
从上往下打印出二叉树的每个节点,同层节点从左至右打印。

//第一种做法 public class Solution { public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList <Integer> li=new ArrayList<Integer>(); ArrayList <TreeN......

南桥北木
20分钟前
0
0
linux 服务管理 Crontba、Ntpdate、Logrotate、Supervisor

crond linux 系统则是由 cron (crond) 这个系统服务来控制的。Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的。 另外, 由于使用者自己也可以设置计划任务,所以,...

狼王黄师傅
50分钟前
3
0
Sobel算子和Scharr滤波器

Sobel算子在数学上的本质是微分,对离散信号,是求邻域内的增量。 基本原理:在图像上,对图像信号在某点进行微分,表示图像的某个特征(如,强度、色调或者饱和度)在该点的变换程度。以强度...

yepanl
今天
2
0
Jenkins API 使用

Jenkins 是一款流行的开源持续集成工具,可以用来做一些软件开发的自动化工作,如打包,测试,自动部署等。 Jenkins 中有 view 和 job 的概念, view 相当于组, job 则是具体的任务。 view...

YanWen
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部