项目中使用定时任务处理业务是很常见的需求,现整理了一下Java传统定时任务的几种实现
一.分类
从实现的技术上来分类,目前主要有三种技术(或者说有三种产品)
-
Java自带的java.util.Timer类
这个类允许你调度一个java.util.TimerTask任务。 使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。 一般用的较少,这篇文章将不做详细介绍。
-
Quartz
这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行 配置起来稍显复杂,稍后会详细介绍。
-
Spring Task
Spring3.0以后自带的task 可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多,稍后会介绍。
从作业类的继承方式,可以分为两类
-
作业类需要继承特定的作业类基类
Quartz中需要继承自org.springframework.scheduling.quartz.QuartzJobBean java.util.Timer中需要继承自java.util.TimerTask
-
作业类是普通的java类,不需要继承任何基类
个人推荐使用这种方式,因为这样所有的类都是普通类,不需要区别对待,保证程序的扩展性
按照作业触发时机,可以分为两类
-
每隔指定时间则触发一次
在Quartz中对应的触发器为:org.springframework.scheduling.quartz.SimpleTriggerBean
-
每到指定时间则触发一次
在Quartz中对应的调度器为:org.springframework.scheduling.quartz.CronTriggerBean
注意⚠️:并非每种任务都可以使用这两种触发器,如:Java.util.TimerTask任务就只能使用第一种,Quartz和Spring Task都可以支持这两种触发条件。
二.使用
现详细介绍一下Quartz和Spring Task如何使用
使用Quartz
- Quartz是OpenSymphony开源组织在Job scheduling领域的开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的日程序表
- Quartz是一个任务日程管理系统,一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统
- Quartz用一个小Java库发布文件(.jar文件),这个库文件包含了所有Quartz核心功能。这些功能的主要接口(API)是Scheduler接口。它提供了简单的操作,例如:将任务纳入日程或者从日程中取消,开始/停止/暂停日程进度
Quartz中的核心对象
Scheduler -- 核心调度器
Job -- 任务
JobDetail -- 任务描述
Trigger -- 触发器
- scheduler是一个计划调度器容器,容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。
- JobDetail是一个可执行的工作,它本身可能是有状态的。
- Trigger代表一个调度参数的配置,什么时候去调。
- 当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。
- scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率。
Java中使用Quartz示例
1.项目中引入maven依赖
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
2.编写作业类
Java中可以使用两种实现方式
作业类继承自特定的基类:org.springframework.scheduling.quartz.QuartzJobBean(不推荐,就不写实现了)
-
作业类不继承自特定的基类
Spring能够支持这种方式,归功于两个类:org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
这两个类分别对应spring支持的两种实现任务调度的方式,即前文提到到java自带的timer task方式和Quartz方式。
这里我只写MethodInvokingJobDetailFactoryBean的用法,使用该类的好处是,我们的任务类不再需要继承自任何类,而是普通的pojo,作业类代码如下:package com.chainfin.ssmdemo.tasks; public class QuartzJob { public void task1(){ System.out.println("Hello Quartz"); } }
3.配置作业类
<bean id="myJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<bean class="com.chainfin.ssmdemo.tasks.QuartzJob" />
</property>
<property name="targetMethod" value="task1" />
<property name="concurrent" value="false" /><!-- 作业不并发调度 -->
</bean>
4.配置触发器
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myJob" />
<property name="cronExpression" value="*/10 * * * * ?" />
</bean>
5.配置调度工厂
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
运行效果如下:
使用Spring Task
Spring Task是Spring3.0以后自主开发的定时任务工具,可以将它比作一个轻量级的Quartz,而且使用起来很简单,除spring相关的包外不需要额外的包,而且支持注解和配置文件两种
第一种配置文件方式
1.编写任务类
package com.chainfin.ssmdemo.tasks;
import org.springframework.stereotype.Component;
@Component
public class SpringTaskJob {
public void task1(){
System.out.println("Hello Spring Task1 ......");
}
}
2.在spring配置文件中设置具体的任务
<task:scheduled-tasks>
<task:scheduled ref="springTaskJob" method="task1" cron="*/10 * * * * ?"/>
</task:scheduled-tasks>
注意⚠️:需要在配置文件头部增加task的命名空间及描述,如果你的开发工具是IDEA,会自动提示你引入对应的命名空间及描述,引入即可
运行效果:
第二种:使用注解@Scheduled形式
也许我们不想每写一个任务类还要在xml文件中配置下,我们可以使用注解@Scheduled,我们看看源文件中该注解的定义:
Java代码 :
@Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scheduled {
public abstract String cron();
public abstract long fixedDelay();
public abstract long fixedRate();
}
可以看出该注解有三个方法或者叫参数,分别表示的意思是:
1.cron:指定cron表达式
2.fixedDelay:官方文档解释:An interval-based trigger where the interval is measured from the completion time of the previous task. The time unit value is measured in milliseconds.即表示从上一个任务完成开始到下一个任务开始的间隔,单位是毫秒。
3.fixedRate:官方文档解释:An interval-based trigger where the interval is measured from the start time of the previous task. The time unit value is measured in milliseconds.即从上一个任务开始到下一个任务开始的间隔,单位是毫秒。
1.编写任务类
package com.chainfin.ssmdemo.tasks;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class SpringTaskJob {
public void task1() {
System.out.println("Hello Spring Task1 ......");
}
@Scheduled(cron = "*/10 * * * * ?")
public void task2() {
System.out.println("Hello Spring Task2 ......");
}
}
2.在Spring配置文件中打开注解驱动,使@Scheduled注解生效
<!--开启这个配置,spring才能识别@Scheduled注解-->
<task:annotation-driven />
运行效果: