Spring之定时任务 @Scheduled注解入门

背景

最近帮人做点东西,主要完成功能如下:

两张表,一张信息明细表,一张发送任务表。需要做两个定时任务,一个循环去读取信息明细表,将没有读取过的信息读取到任务表中;一个循环去读取任务表,将没有发送过的消息发送出去。

功能简单来说,就是这样子。一开始用的是让任务类继承TimerTask,实现run方法,然后再去调用任务类。
如下:

public class MyTask extends TimerTask {
	@Override
	public void run() {
		// todo...
		System.out.println("执行任务");
	}

	public static void main(String[] args) {
		Timer timer = new Timer();
    	timer.scheduleAtFixedRate(new MyTask(), 0, 1000); // 每1秒执行一次,也可以在xml中配置
	}
}

这时候又遇到一个问题:调用service时,没法注入。
原因:继承TimerTask后,会直接运行run()方法,还没来得及执行注解进行注入。
解决:引入了SpringContextUtil的工具类。(具体不讨论,可以百度一下,啊哈哈~~)

最终还是能够实现功能,但是代码既不美观,又很复杂。哈哈
后面再搜了一下,发现spring中的@Scheduled注解实在是太方便了,所以记录下来,加深印象,以后还能回顾一下。

实现

1、在spring配置文件中添加相应配置,以支持定时任务的注解实现
  • 在xml里加入task的命名空间
<!-- beans里添加:-->  
xmlns:task="http://www.springframework.org/schema/task" 
<!-- xsi:schemaLocation里添加:-->  
http://www.springframework.org/schema/task  
http://www.springframework.org/schema/task/spring-task-3.1.xsd 

xml中加入命名空间

  • 配置注解驱动的定时任务
<!-- 启用定时任务 -->
<task:annotation-driven scheduler="scheduler"/> 
<!-- 配置定时任务的线程池,spring定时任务默认单线程,推荐配置线程池,若不配置多任务下会有问题。 -->
<task:scheduler id="scheduler" pool-size="5" />

在这里插入图片描述
3. 配置注解扫描路径
这个需要指定,当我把任务放在controller层里面时,明明springmvc.xml中已经配置了路径,可是还是不行,需要在spring配置文件中配置,定时任务才能正常启动起来。

<context:component-scan base-package="com.xxx.xxx"
2、在代码中加上注解

需要有两个注解 @Component、@Scheduled

@Component
@Lazy(false) 
public class Test {  
    @Scheduled(cron = "0/5 * * * * ?")    
    public void showTime() {    
        System.out.println(new Date());    
    }   
} 

此时,将项目跑起来就能成功调起定时任务了。

注意点:

  1. 类中如果引用其他service层,用属性注入@Autowired即可(与TimerTask不同)。但是如果通过main来调用,则不能用@Autowired注解(未启用项目),此时可用getBean来获取实例对象。
  2. 定时器的任务方法不能有返回值,如果有返回值,spring初始化的时候报错误:Only void-returning methods may be annotated with @Scheduled。
  3. 定时器的任务方法不能有参数,如果有参数,启动时会报错:Only no-arg methods may be annotated with @Scheduled
    CRON表达式。

@Scheduled参数部分:

  1. cron:一个类似cron的表达式,扩展了通常的UN * X定义,包括秒、分、时、星期、月、年的触发器。(等待上一次任务完成,在开始时间之后周期倍数时间后才开始下一次任务)
    @Scheduled(cron = “0/5 * * * * ?”),开始时间00:00:00,结束时间00:00:06,那么下一次开始时间为00:00:10
  2. fixedRate:在调用之间以固定的周期(以毫秒为单位)执行带注释的方法。(等待上一次任务完成,如果已超过周期时间,就马上执行,如果还没到周期时间,则等待周期时间结束执行)
    @Scheduled(fixedRate = 5 * 1000),开始时间00:00:00,结束时间00:00:06,那么下一次开始时间为00:00:06
  3. fixedDelay:在最后一次调用结束和下一次调用开始之间以固定周期(以毫秒为单位)执行带注释的方法。(等待上一次任务完成,完成时间再过周期时间后,开始下一次任务)
    @Scheduled(fixedDelay = 5 * 1000),开始时间00:00:00,结束时间00:00:06,那么下一次开始时间为00:00:11

如果是强调任务间隔的定时任务,建议使用fixedRate和fixedDelay,如果是强调任务在某时某分某刻执行的定时任务,建议使用cron表达式

详细解释可以看一下这篇文章学习一下:如果我的任务在5秒内没有执行完呢?spring会怎么处理呢?


CRON表达式实例:

"0 0 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分每分钟一次触发 
"0 0/5 14 * * ?"    每天从下午2点开始到2:55分结束每5分钟一次触发 
"0 0/5 14,18 * * ?"    每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发 
"0 0-5 14 * * ?"    每天14:00至14:05每分钟一次触发 
"0 10,44 14 ? 3 WED"    三月的每周三的14:10和14:44触发 
"0 15 10 ? * MON-FRI"    每个周一、周二、周三、周四、周五的10:15触发 

另附(在线cron表示式生成器:http://cron.qqe2.com/)
更复杂的表达式可以参考这篇文章,spring如何设置定时任务详解(@Scheduled)。这篇文章还从源码分析了执行过程,很值得学习!

3. 问题

后面又遇到一些问题:

  1. 启动定时时,出现bean循环依赖的情况,当前任务类有两个service注入,其中一个service还依赖于另一个service
    解决:加Lazy延迟加载注解

    @Autowired
    @Lazy
    private ServiceA serviceA;

  2. 针对我的项目背景,扫描表的时候,可能遇到多个任务堆积,但是其实扫描表的动作,其实我只需要他再执行一次就可以了。现在还没遇到问题,所以还没有想方法去解决。留待后面回头再看,嘻嘻~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值