以前使用PHP的时候,定时脚本都是使用Linux的crontab来执行PHP脚本的。。。比较原始,而且精度是分钟。现在使用Spring了,可以使用Spring的定时任务机制。

Spring中有两种方式来执行定时任务,一个是传统的结合Quartz的做法,一个是3.0之后的@Scheduled注解做法,我们都来看一下。

使用注解

Spring3中添加了@Scheduled注解来简化定制任务的配置。只要简单的一个注解就可以实现定时任务,简直简单到爆。

Spring的定时任务的代码集成在context这个核心类库中,所以就不需要引入其他依赖了。

开启定时任务

需要在Spring应用上下文中添加配置才能开启后台任务:

1
2
3
4
5
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"


<task:annotation-driven />

注解需要被调度的函数

用Scheduled标注对应的函数,Spring就会按照配置指定执行函数:

1
2
3
4
5
6
7
8
//每隔一秒执行一次
@Scheduled(fixedRate = 1000)

//距离上次执行完一秒执行一次
@Scheduled(fixedDelay = 1000)

//使用Cron表达式来指定频率
@Scheduled(cron = "1 * * * * ?")

最后的Cron表达式类似于Crontab的表达式,不过精确到秒。具体参考:Cron Expressions

然后就搞定了!简直比Crontab还简单。

使用Quartz

除了使用Spring3提供的Scheduled注解,还有一种更传统的做法就是结合使用Quartz这个调度器。这种做法配置量比较大,个人不是很喜欢。

添加依赖

Spring在spring-context-support这个库中提供了Quartz的支持,所以需要引入spring-context-support和quartz的依赖:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>

定时任务配置

配置分为三个部分,作业类,触发器和调度工厂:

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
<bean id="qtask" class="task.QTask" />

<!-- 配置作业类 -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!-- 指定作业类 -->
<property name="targetObject" ref="qtask" />
<!-- 指定作业方法 -->
<property name="targetMethod" value="task" />
<!-- 设置是否并发执行作业 -->
<property name="concurrent" value="true" />
</bean>

<!-- 配置作业调度的方式(触发器) -->
<bean id="triggers" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!-- 指定作业 -->
<property name="jobDetail" ref="jobDetail" />
<!-- 指定Cron表达式 -->
<property name="cronExpression" value="* * * * * ?" />
</bean>

<!-- 配置调度工厂 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- 设置调度器开始工作的延迟,单位秒 -->
<property name="startupDelay" value="10"/>
<!-- 指定需要触发的触发器 -->
<property name="triggers">
<list>
<ref bean="triggers" />
</list>
</property>
</bean>

这里重点说一下concurrent这个配置,他指定是否可以并发执行任务。比如我设置一秒执行一次任务,但是任务执行一次需要一分钟,那么为了保证没秒都能执行一次任务,Quartz会起新的线程来执行任务。而如果concurrent设置为false,那么同时只会有一个任务在执行,即使已经破坏了设置的频率,也保证只有一个任务在执行。

问题:被delay的任务,是被缓存还是被抛弃?
实测:估计是没有“被delay的任务”的概念,如果concurrent=false,那么执行期间不会去触发,执行完成后才会再次开始触发。
测试用例:一个任务第一次执行一分钟,其他执行基本不耗时。cron=”/5 * ?”,第一次执行完毕后,不会一次性打印出多个后续任务,而是再过了5s后打印。

任务代码

任务代码就是普通的类了:

1
2
3
4
5
public class QTask {
public void task(){
System.out.println("q task");
}
}

执行程序,载入Spring运行上下文,任务就会被定时执行了。

参考资料