本文参考自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,直接抛出异常;如果当系统忙时丢弃某些任务是可接受的,可以使用DiscardPolicyDiscardOldestPolicy策略;当系统负载较重时还可以使用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 定时任务相关推荐

  1. Spring定时任务的几种实现

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

  2. (转)Spring定时任务的几种实现

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

  3. spring定时任务

    使用spring定时任务包Quartz时,必须使用Quartz1.85以下版本的. 查看发现spring3.0.5中org.springframework.scheduling.quartz.Cron ...

  4. Spring定时任务并行(异步)处理

    最近项目中遇到一个问题 , 在SpringBoot中设置了定时任务之后 , 在某个点总是没有执行 . 经过搜索研究发现 , spring 定时器任务scheduled-tasks默认配置是单线程串行执 ...

  5. Spring定时任务@scheduled多线程的使用(@Async注解)

    1.开篇 在Spring定时任务@Scheduled注解使用方式浅窥这篇文章里面提及过,spring的定时任务默认是单线程的,他在某些场景下会造成堵塞,那么如果我们想让每一个任务都起一条线程去执行呢? ...

  6. spring定时任务执行两次的原因与解决方法

    spring定时任务执行两次的原因与解决方法 参考文章: (1)spring定时任务执行两次的原因与解决方法 (2)https://www.cnblogs.com/yolanda-lee/p/7339 ...

  7. Spring定时任务高级使用篇

    Spring定时任务高级使用篇 前面一篇博文 <Spring之定时任务基本使用篇> 介绍了Spring环境下,定时任务的简单使用姿势,也留了一些问题,这一篇则希望能针对这些问题给个答案 I ...

  8. spring定时任务需要在项目启动时执行一次

    spring定时任务需要在项目启动时执行一次,然后再按照指定规则执行 在定时任务方法上加注解@PostConstruct,不是spring提供的注解,是JAVA原生注解,在初始化servlet之前执行 ...

  9. 浅谈Spring定时任务

    浅谈Spring定时任务 三种定时任务基于原理 多定时任务并发配置 动态定时任务 定时任务Demo 三种定时任务基于原理 SpringBoot配置定时任务主要有Spring Schedule.JDK自 ...

  10. 解决spring定时任务执行两次和tomcat部署缓慢的问题

    解决spring定时任务执行两次和tomcat部署缓慢的问题 参考文章: (1)解决spring定时任务执行两次和tomcat部署缓慢的问题 (2)https://www.cnblogs.com/Si ...

最新文章

  1. python中平均值函数_python自定义函数ma(x,y)求简单平均值输出结果到列表
  2. 设计模式之工厂模式和抽象工厂模式
  3. FW 每秒百万级别的 HTTP 请求 sung: 重型的(heavy-duty)、分布式的、多协议测试工具...
  4. 并不对劲的概率与期望
  5. Linux 网络及IP概述
  6. 【WebRTC---入门篇】(十四)WebRTC音视频录制
  7. 调用本地电脑摄像头并进行按P进行捕获照片并保存,按下Q退出
  8. Power Platform之Power Automate新增RPA功能
  9. 设置 shell 脚本中 echo 显示内容带颜色
  10. linux7 vi 末行 快捷键,vi 常用操作快捷键
  11. 10岁女程序员,婉拒谷歌Offer,研发全球首款AI桌游,现在是一名CEO
  12. 组件实例对象与Vue实例对象
  13. 微信公众号连接服务器显示404,WordPress 微信机器人自动回复显示 404 错误解决办法...
  14. 【转】实用API大全
  15. cocos creator 游戏源码_Cocos Creator 3D v1.0.2 正式发布,新增小游戏平台支持
  16. 用telnet逛bbs
  17. Keras安装+Pycharm配置Keras
  18. 接口测试基础、流程、工具
  19. [ 数通面试 ] 奇安信技术支持工程师 面试分享
  20. 从市场换手率变化判断头部

热门文章

  1. # 20155224 实验四 Android程序设计
  2. java,andoid安卓去掉替换字符串中的空字符空格换行等
  3. oracle v$sysstat性能视图
  4. 成都Uber优步司机奖励政策(3月31日)
  5. xssProject在java web项目中应用
  6. 有哪些问题应该得到解决?
  7. MyStringTokenize
  8. 解决能上QQ不能上网页的批处理〖罗斌原创〗
  9. 【转贴】ListView控件学习系列2-编辑ListView
  10. Android 百度地图开发(三)--- 实现比例尺功能和替换自带的缩放组件