Spring 定时任务
本文参考自Spring官方文档 34. Task Execution and Scheduling。
在程序中常常有定时任务的需求,例如每隔一周生成一次报表、每个月月末清空用户积分等等。Spring也提供了相应的支持,我们可以非常方便的按时执行任务。
项目准备
这里我使用Gradle来建立项目,然后在build.gradle
中添加下面一行。springVersion的值是目前最新的Spring版本'4.3.7.RELEASE'
。使用Maven的话也添加相应的行。spring-context会自动引入spring-core等几个最基本的依赖。
compile group: 'org.springframework', name: 'spring-context', version: springVersion
定时任务属于Spring的核心支持部分,所以我们不需要再添加其他的依赖了。所以定时任务功能既可以在命令行程序中使用,也可以在Java Web程序中使用。当然后者可能使用的更广泛一些(毕竟Web程序需要一直运行的嘛)。
这里我们定义两个任务,后面会让它们可以定时执行。
public interface IService {void doService();
}public class SimpleService implements IService {@Overridepublic void doService() {LocalTime time = LocalTime.now();System.out.println("This is a simple service:" + time);}
}public class ExpensiveTaskService implements IService {@Overridepublic void doService() {try {Thread.sleep(TimeUnit.SECONDS.toMillis(1));LocalTime time = LocalTime.now();System.out.println("This is an expensive task:" + time);} catch (InterruptedException e) {e.printStackTrace();}}
}
Spring的任务抽象
TaskExecutor
TaskExecutor
接口是任务执行接口,类似于java.util.concurrent.Executor
,该接口只有一个方法execute(Runnable task)
,用于执行任务。
Spring提供了一组TaskExecutor的实现,详细列表可以看这里34.2.1. TaskExecutor types。要使用它们也很简单,直接注册为Spring Bean,然后注入到程序中即可使用。
TaskScheduler
TaskScheduler
接口是定时器的抽象,它的源代码如下。可以看到,该接口包含了一组方法用于指定任务执行的时间。
public interface TaskScheduler {ScheduledFuture schedule(Runnable task, Trigger trigger);ScheduledFuture schedule(Runnable task, Date startTime);ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);ScheduledFuture scheduleAtFixedRate(Runnable task, long period);ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);}
Spring提供了两个实现,一是TimerManagerTaskScheduler
,会将任务代理到CommonJ TimerManager实例。第二个是ThreadPoolTaskScheduler
,当我们不需要管理线程的时候就可以使用该类。而且它还同时实现了TaskExecutor
接口,所以一个ThreadPoolTaskScheduler
实例即可同时用于执行定时任务。
Trigger
在定时器接口的方法中我们可以发现一个方法接受Trigger接口, 而Trigger也是一个接口,抽象了触发任务执行的触发器。
Trigger接口有两个实现,先说说比较简单的一个PeriodicTrigger
。它直接按照给定的时间间隔触发任务执行。更常用的一个触发器是CronTrigger
,它使用Cron表达式指定何时执行任务。下面是Spring官方的一个例子。
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
关于Cron表达式的信息可以参考这篇博客QuartZ Cron表达式。另外还有一个可以在线生成Cron表达式的网站:CroMaker,不过好像需要XX才能访问。而且好像Spring不支持第二个星期一这样的定时器设置,所以如果有这样的需求,需要使用Quartz。
配置任务
任务配置既可以使用Java配置,也可以使用XML配置。不管使用哪种方法,首先需要将要执行的方法所在的类配置为Spring Bean。例如下面就用XML配置注册了两个要执行的任务。
<bean id="simpleService" class="yitian.study.service.SimpleService"/><bean id="expensiveTaskService"class="yitian.study.service.ExpensiveTaskService"/>
Java配置
定时任务
首先看看Java配置。我们需要在配置类上添加@EnableScheduling,如果需要异步的定时任务,还需要添加@Async。
@Configuration
@EnableAsync
@EnableScheduling
public class TaskConfiguration {
}
然后在要执行的方法上添加@Scheduled注解。@Scheduled注解有几个参数,任务会在相应参数的时间下执行。cron参数指定Cron表达式;fixedDelay指定任务执行的间隔,单位是毫秒;initialDelay指定当程序启动后多长时间开始执行第一次任务,单位是毫秒;zone指定任务执行时间所在的时区。下面的例子简单的指定了每隔一秒重复执行一次任务。
public class SimpleService implements IService {@Scheduled(fixedDelay = 1000)@Overridepublic void doService() {LocalTime time = LocalTime.now();System.out.println("This is a simple service:" + time);}
}
异步任务
然后是异步任务,如果任务执行时间比较长的话,我们可以考虑使用异步的任务。当调用异步任务的时候,异步方法直接返回,异步任务会交由相应的任务执行器来执行。在Spring中标记异步方法很简单,直接在方法上使用@Async注解。如果需要指定异步方法使用的执行器,可以向注解传递执行器的名称。异步方法可以返回空值。
@Async("otherExecutor")
void doSomething(String s) {// this will be executed asynchronously by "otherExecutor"
}
但是如果异步方法想返回其他值的话,就必须使用Future。不过不仅是java.util.concurrent.Future
,异步方法还可以返回Spring的org.springframework.util.concurrent.ListenableFuture
和JDK8的java.util.concurrent.CompletableFuture
类型。
@Async
Future<String> returnSomething(int i) {// this will be executed asynchronously
}
异步方法不仅可以用于定时任务中,在Spring的其他地方也可以使用。例如Spring Data JPA可以使用@Async编写异步的查询方法。
需要注意,异步方法没有对应的XML配置,如果我们想让方法是异步的,只能使用注解。当然也不是完全不行,不过就比较麻烦了,你需要使用AsyncExecutionInterceptor
和AOP配合才能达到类似的效果。
如果需要处理异步方法的异常,我们需要实现一个AsyncUncaughtExceptionHandler
。下面的异步异常处理器简单的打印异常信息。
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {ex.printStackTrace();}
}
然后通过实现AsyncConfigurer
接口(Java配置方式)或者task:annotation-driven
(XML配置方式)的exception-handler
元素来配置。
XML配置
Spring提供了task命名空间,让配置定时任务非常简单。
定时器
task:scheduler
会注册一个ThreadPoolTaskScheduler
定时器,它只有一个属性线程池大小。默认是1,我们需要根据任务的数量指定一个合适的大小。
<task:scheduler id="threadPoolTaskScheduler"pool-size="10"/>
执行器
task:executor
会注册一个ThreadPoolTaskExecutor
执行器,我们可以使用它的相关属性来配置该执行器。默认情况下执行队列是无限的,可能会导致JVM使用完所有内存。因此我们最好指定一个确定的数值。还有一个rejection-policy
属性,指定执行器队列满时的执行策略:默认是AbortPolicy
,直接抛出异常;如果当系统忙时丢弃某些任务是可接受的,可以使用DiscardPolicy
或DiscardOldestPolicy
策略;当系统负载较重时还可以使用CallerRunsPolicy
,它不会将任务交给执行器线程,而是让调用者线程来执行该任务。最后一个就是keep-alive
属性,也就是超出线程池数量 线程完成任务之后的存活时间,单位是秒。
<task:executor id="threadPoolTaskExecutor"pool-size="10"queue-capacity="10"/>
执行任务
执行任务很简单,使用<task:scheduled-tasks>
指定要执行的Bean和方法即可。
<task:scheduled-tasks><task:scheduled ref="simpleService" method="doService"cron="*/1 * * * * *"/><task:scheduled ref="expensiveTaskService" method="doService"cron="*/2 * * * * *"/></task:scheduled-tasks>
要设置定时的话,只需要指定相应的属性即可。
<task:scheduled-tasks scheduler="myScheduler"><task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/><task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/><task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/>
</task:scheduled-tasks><task:scheduler id="myScheduler" pool-size="10"/>
Quartz集成
Quartz是一个定时任务的库。Spring也提供了它的支持。Quartz的使用方法请查阅相应文档。这里只简单介绍一下。
Spring的Quartz集成在spring-context-support
包中,它还需要Spring事务的支持。因此我们需要下面这样的依赖声明。
compile group: 'org.springframework', name: 'spring-tx', version: springVersioncompile group: 'org.springframework', name: 'spring-context-support', version: springVersioncompile group: 'org.quartz-scheduler', name: 'quartz', version: '2.2.3'
定义任务
Quartz的任务需要继承Quartz的Job接口。所以一个典型的任务可以写成这样。
public class QuartzService implements IService, Job {@Overridepublic void doService() {System.out.println("This is a quartz service");}@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Do something in execute method of quartz");}
}
JobDetailFactoryBean
JobDetailFactoryBean用来定义实现了Job接口的任务。如果需要添加更多信息,可以使用jobDataAsMap
属性设置。
<bean id="jobDetail"class="org.springframework.scheduling.quartz.JobDetailFactoryBean"><property name="jobClass" value="yitian.study.service.QuartzService"/><property name="jobDataAsMap"><map><entry key="timeout" value="10"/></map></property>
</bean>
MethodInvokingJobDetailFactoryBean
如果任务没有实现Job接口,也可以执行,这时候需要使用MethodInvokingJobDetailFactoryBean。如果存在任务对象,使用targetObject
属性,如果有任务类,使用targetClass
属性。
<bean id="methodJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"><property name="targetObject" ref="quartzService"/><property name="targetMethod" value="doService"/><property name="concurrent" value="true"/>
</bean>
触发器
有了任务,就可以定义触发器了。触发器有两个:SimpleTriggerFactoryBean
,以指定的间隔重复执行任务;CronTriggerFactoryBean
,以给定的Cron表达式执行任务。Quartz的Cron表达式比Spring 的强大,它支持第几个星期几这样的Cron表达式。
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"><property name="jobDetail" ref="jobDetail"/><property name="startDelay" value="0"/><property name="repeatInterval" value="1000"/>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"><property name="jobDetail" ref="methodJobDetail"/><property name="cronExpression" value="*/2 * * * * ?"/>
</bean>
执行任务
有了触发器,我们就可以执行任务了。注册一个SchedulerFactoryBean
,然后将触发器的Bean引用传入即可。
<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="triggers"><list><ref bean="cronTrigger"/><ref bean="simpleTrigger"/></list></property>
</bean>
Spring 定时任务相关推荐
- Spring定时任务的几种实现
Spring定时任务的几种实现 spring框架 quartz spring spring-task 定时任务 注解 近日项目开发中需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信 ...
- (转)Spring定时任务的几种实现
Spring定时任务的几种实现 博客分类: spring框架 quartzspringspring-task定时任务注解 Spring定时任务的几种实现 近日项目开发中需要执行一些定时任务,比如需要 ...
- spring定时任务
使用spring定时任务包Quartz时,必须使用Quartz1.85以下版本的. 查看发现spring3.0.5中org.springframework.scheduling.quartz.Cron ...
- Spring定时任务并行(异步)处理
最近项目中遇到一个问题 , 在SpringBoot中设置了定时任务之后 , 在某个点总是没有执行 . 经过搜索研究发现 , spring 定时器任务scheduled-tasks默认配置是单线程串行执 ...
- Spring定时任务@scheduled多线程的使用(@Async注解)
1.开篇 在Spring定时任务@Scheduled注解使用方式浅窥这篇文章里面提及过,spring的定时任务默认是单线程的,他在某些场景下会造成堵塞,那么如果我们想让每一个任务都起一条线程去执行呢? ...
- spring定时任务执行两次的原因与解决方法
spring定时任务执行两次的原因与解决方法 参考文章: (1)spring定时任务执行两次的原因与解决方法 (2)https://www.cnblogs.com/yolanda-lee/p/7339 ...
- Spring定时任务高级使用篇
Spring定时任务高级使用篇 前面一篇博文 <Spring之定时任务基本使用篇> 介绍了Spring环境下,定时任务的简单使用姿势,也留了一些问题,这一篇则希望能针对这些问题给个答案 I ...
- spring定时任务需要在项目启动时执行一次
spring定时任务需要在项目启动时执行一次,然后再按照指定规则执行 在定时任务方法上加注解@PostConstruct,不是spring提供的注解,是JAVA原生注解,在初始化servlet之前执行 ...
- 浅谈Spring定时任务
浅谈Spring定时任务 三种定时任务基于原理 多定时任务并发配置 动态定时任务 定时任务Demo 三种定时任务基于原理 SpringBoot配置定时任务主要有Spring Schedule.JDK自 ...
- 解决spring定时任务执行两次和tomcat部署缓慢的问题
解决spring定时任务执行两次和tomcat部署缓慢的问题 参考文章: (1)解决spring定时任务执行两次和tomcat部署缓慢的问题 (2)https://www.cnblogs.com/Si ...
最新文章
- python中平均值函数_python自定义函数ma(x,y)求简单平均值输出结果到列表
- 设计模式之工厂模式和抽象工厂模式
- FW 每秒百万级别的 HTTP 请求 sung: 重型的(heavy-duty)、分布式的、多协议测试工具...
- 并不对劲的概率与期望
- Linux 网络及IP概述
- 【WebRTC---入门篇】(十四)WebRTC音视频录制
- 调用本地电脑摄像头并进行按P进行捕获照片并保存,按下Q退出
- Power Platform之Power Automate新增RPA功能
- 设置 shell 脚本中 echo 显示内容带颜色
- linux7 vi 末行 快捷键,vi 常用操作快捷键
- 10岁女程序员,婉拒谷歌Offer,研发全球首款AI桌游,现在是一名CEO
- 组件实例对象与Vue实例对象
- 微信公众号连接服务器显示404,WordPress 微信机器人自动回复显示 404 错误解决办法...
- 【转】实用API大全
- cocos creator 游戏源码_Cocos Creator 3D v1.0.2 正式发布,新增小游戏平台支持
- 用telnet逛bbs
- Keras安装+Pycharm配置Keras
- 接口测试基础、流程、工具
- [ 数通面试 ] 奇安信技术支持工程师 面试分享
- 从市场换手率变化判断头部