SpringBoot-定时任务
在我们开发项目中,定时任务是经常用到的一种技术,来处理一些业务,SpringBoot 默认支持定时任务,怎么样是不是感觉 Spring Boot 太人性化了,那么下面我们看一下怎么实现一个定时器吧。
Tips
开发 Spring Boot 项目有个口诀或者说是 Spring Boot 的三板斧:加依赖、写配置、添注解
示例代码详见:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-schedule
加依赖
Spring Boot 默认支持定时任务,所以只要加入 Spring Boot 的依赖即可1
2
3
4<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
写配置
实际开发中一般将经常变化的值配置到配置文件中,定时任务中我们可以自定义配置项 cron 来控制定时任务执行的时间1
2
3
4
5
6
7server:
  port: 8080
spring:
  application:
    name: schedule
schedule:
  cron: 0 */1 * * * ? # 表达式表示 1 分钟执行一次
关于 cron 表达式,如果不清楚请先百度。
添注解
我们需要在启动类上加 @EnableScheduling 注解即可开启定时任务1
2
3
4
5
6
7@SpringBootApplication
@EnableScheduling
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
编写定时任务的代码
1  | @Slf4j  | 
启动项目,便会在控制台隔1分钟打印1
2
3
4哈哈,我执行了!
哈哈,我执行了!
哈哈,我执行了!
...
注意: @Component 注解是启动后立即执行,${schedule.cron} 便是从配置文件读取配置的执行时间。
前面说了如果你对 cron 不了解,不用担心,Spring boot 提供了另外的方式 fixedRate,详解如下:1
2
3@Scheduled(fixedRate = 60000) :上一次开始执行时间点之后1分再执行
@Scheduled(fixedDelay = 60000) :上一次执行完毕时间点之后1分再执行
@Scheduled(initialDelay=1000, fixedRate=60000) :第一次延迟1秒后执行,之后按 fixedRate 的规则每1分执行一次
代码:1
2
3
4@Scheduled(fixedRate = 60000)
public void testTask1() {
    log.info("哈哈,我又执行了!");
}
结果:1
2
3
4
5哈哈,我执行了!
哈哈,我又执行了!
哈哈,我执行了!
哈哈,我又执行了!
...
动态添加或删除定时任务
在实际项目中,往往会遇到动态的添加或停止定时任务,来我们看看怎么实现,首先添加一个配置类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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80@Configuration
public class DefaultSchedulingConfigurer implements SchedulingConfigurer {
    private ScheduledTaskRegistrar taskRegistrar;
    private Set<ScheduledFuture<?>> scheduledFutures = null;
    private Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>();
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        this.taskRegistrar = taskRegistrar;
        //System.out.println(inited());
    }
    @SuppressWarnings("unchecked")
    private Set<ScheduledFuture<?>> getScheduledFutures() {
        if (scheduledFutures == null) {
            try {
                // spring版本不同选用不同字段scheduledFutures
                scheduledFutures = (Set<ScheduledFuture<?>>) BeanUtils.getProperty(taskRegistrar, "scheduledTasks");
            } catch (NoSuchFieldException e) {
                throw new SchedulingException("not found scheduledFutures field.");
            }
        }
        return scheduledFutures;
    }
    /**
     * 添加任务
     */
    public void addTriggerTask(String taskId, TriggerTask triggerTask) {
        if (taskFutures.containsKey(taskId)) {
            throw new SchedulingException("the taskId[" + taskId + "] was added.");
        }
        TaskScheduler scheduler = taskRegistrar.getScheduler();
        ScheduledFuture<?> future = scheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());
        getScheduledFutures().add(future);
        taskFutures.put(taskId, future);
    }
    /**
     * 取消任务
     */
    public void cancelTriggerTask(String taskId) {
        ScheduledFuture<?> future = taskFutures.get(taskId);
        if (future != null) {
            future.cancel(true);
        }
        taskFutures.remove(taskId);
        getScheduledFutures().remove(future);
    }
    /**
     * 重置任务
     */
    public void resetTriggerTask(String taskId, TriggerTask triggerTask) {
        cancelTriggerTask(taskId);
        addTriggerTask(taskId, triggerTask);
    }
    /**
     * 任务编号
     */
    public Set<String> taskIds() {
        return taskFutures.keySet();
    }
    /**
     * 是否存在任务
     */
    public boolean hasTask(String taskId) {
        return this.taskFutures.containsKey(taskId);
    }
    /**
     * 任务调度是否已经初始化完成
     */
    public boolean inited() {
        return this.taskRegistrar != null && this.taskRegistrar.getScheduler() != null;
    }
}
然后新建一个 controller1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestTaskController {
    
    private DefaultSchedulingConfigurer defaultSchedulingConfigurer;
    
    public String add( String name) {
        defaultSchedulingConfigurer.addTriggerTask(name,
                new TriggerTask(
                        () -> System.out.println("hello world!"),
                        new CronTrigger("0/5 * * * * ? ")));//5秒执行一次
        return "任务开启成功";
    }
    
    public String del( String name) {
        defaultSchedulingConfigurer.cancelTriggerTask(name);
        return "任务删除成功";
    }
}
请求 http://localhost:8080/task/add,定时任务即可开启
请求 http://localhost:8080/task/del,定时任务就删除成功了
最后
其实 Spring boot 的定时任务相对比较简单,如果在高并发集群环境下,我们尽量使用框架来支撑我们的业务,下一节我会介绍定时任务的框架 quartz ,大家敬请期待吧。
参考与相关链接
示例代码:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-schedule
Spring boot 定时任务:http://www.ityouknow.com/springboot/2016/12/02/spring-boot-scheduler.html
SpringBoot+schedule+可以动态添加或删除定时任务:https://blog.csdn.net/nicky_lc/article/details/106961779

更多文章请关注微信公众号: zhiheng博客
如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]
