写在前面

SpringBoot创建定时任务的方式很简单,主要有两种方式:一、基于注解的方式(@Scheduled)二、数据库动态配置。实际开发中,第一种需要在代码中写死表达式,如果修改起来,又得重启会显示很麻烦;所以我们往往会采取第二种方式,可以直接从数据库中读取定时任务的指定执行时间,无需重启。

下面就来介绍下这两种方式吧

一、基于注解(@Scheduled)

基于注解是一种静态的方式,只需要几行代码就可以搞定了

添加一个配置类

 1 @Configuration  //标记配置类
2 @EnableScheduling //开启定时任务
3 public class MyScheduleConfig {
4
5 //添加定时任务
6 @Scheduled(cron = "0/5 * * * * ?")
7 private void myTasks() {
8 System.out.println("执行定时任务 " + LocalDateTime.now());
9 }
10 }

上面代码的cron表达式表示每5秒执行一次,可以通过这个网站(https://qqe2.com/cron)去生成要的cron表达式

启动应用,控制台看效果

这个方式的确很简单方便,但前面介绍也说到了,有个缺点就是当我们需要去修改定时任务的执行周期或者停止的时候,我们需要到代码层去修改,重启。

二、数据库动态配置

这里使用MySQL数据库

1、表数据添加,资源配置

1.1 添加表

CREATE TABLE `scheduled_job` (
`job_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`job_key` varchar(128) NOT NULL COMMENT '定时任务完整类名',
`cron_expression` varchar(20) NOT NULL COMMENT 'cron表达式',
`task_explain` varchar(50) NOT NULL DEFAULT '' COMMENT '任务描述',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1:正常;-1:停用',
PRIMARY KEY (`job_id`),
UNIQUE KEY `job_key` (`job_key`),
UNIQUE KEY `cron_key_unique_idx` (`job_key`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='定时任务表';

1.2 插入两条数据,job_key根据是完整的类名

1.3 引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
<scope>runtime</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<!--lombok简化代码-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>

1.4 配置application.yml

spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?userUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: 123
driver-class-name: com.mysql.jdbc.Driver
server:
servlet:
context-path: /demo
port: 8888

2、疯狂贴代码

2.1 创建定时任务线程池

 1 @Configuration
2 @Slf4j
3 public class ScheduledConfig {
4
5 @Bean
6 public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
7 log.info("创建定时任务调度线程池 start");
8 ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
9 threadPoolTaskScheduler.setPoolSize(20);
10 threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
11 threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
12 threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
13 log.info("创建定时任务调度线程池 end");
14 return threadPoolTaskScheduler;
15 }
16
17 }

2.2 项目启动时初始化定时任务

 1 @Slf4j
2 @Component
3 public class ScheduledTaskRunner implements ApplicationRunner {
4 @Autowired
5 private ScheduledTaskService scheduledTaskService;
6
7 @Override
8 public void run(ApplicationArguments args) throws Exception {
9 log.info("----初始化定时任务开始----");
10 scheduledTaskService.initTask();
11 log.info("----初始化定时任务完成----");
12 }
13 }

2.3 定时任务公共接口

1 public interface ScheduledOfTask extends Runnable{
2
3 void execute();
4
5 @Override
6 default void run() {
7 execute();
8 }
9 }

2.4 创建两个定时任务实现类

1 @Component
2 @Slf4j
3 public class TaskJob1 implements ScheduledOfTask{
4 @Override
5 public void execute() {
6 log.info("执行任务1 "+ LocalDateTime.now());
7 }
8 }
1 @Component
2 @Slf4j
3 public class TaskJob2 implements ScheduledOfTask{
4 @Override
5 public void execute() {
6 log.info("执行任务2 "+ LocalDateTime.now());
7 }
8 }

2.5 定时任务管理接口

 1 public interface ScheduledTaskService{
2
3 Boolean start(ScheduledJob scheduledJob);
4
5 Boolean stop(String jobKey);
6
7 Boolean restart(ScheduledJob scheduledJob);
8
9 void initTask();
10 }

2.6 定时任务管理实现类

  1 @Slf4j
2 @Service
3 public class ScheduledTaskServiceImpl implements ScheduledTaskService {
4
5 /**
6 * 可重入锁
7 */
8 private ReentrantLock lock = new ReentrantLock();
9
10 /**
11 * 定时任务线程池
12 */
13 @Autowired
14 private ThreadPoolTaskScheduler threadPoolTaskScheduler;
15
16 /**
17 * 启动状态的定时任务集合
18 */
19 public Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();
20
21 @Autowired
22 private ScheduledJobService scheduledJobService;
23
24 @Override
25 public Boolean start(ScheduledJob scheduledJob) {
26 String jobKey = scheduledJob.getJobKey();
27 log.info("启动定时任务"+jobKey);
28 //添加锁放一个线程启动,防止多人启动多次
29 lock.lock();
30 log.info("加锁完成");
31
32 try {
33 if(this.isStart(jobKey)){
34 log.info("当前任务在启动状态中");
35 return false;
36 }
37 //任务启动
38 this.doStartTask(scheduledJob);
39 } finally {
40 lock.unlock();
41 log.info("解锁完毕");
42 }
43
44 return true;
45 }
46
47 /**
48 * 任务是否已经启动
49 */
50 private Boolean isStart(String taskKey) {
51 //校验是否已经启动
52 if (scheduledFutureMap.containsKey(taskKey)) {
53 if (!scheduledFutureMap.get(taskKey).isCancelled()) {
54 return true;
55 }
56 }
57 return false;
58 }
59
60 @Override
61 public Boolean stop(String jobKey) {
62 log.info("停止任务 "+jobKey);
63 boolean flag = scheduledFutureMap.containsKey(jobKey);
64 log.info("当前实例是否存在 "+flag);
65 if(flag){
66 ScheduledFuture scheduledFuture = scheduledFutureMap.get(jobKey);
67
68 scheduledFuture.cancel(true);
69
70 scheduledFutureMap.remove(jobKey);
71 }
72 return flag;
73 }
74
75 @Override
76 public Boolean restart(ScheduledJob scheduledJob) {
77 log.info("重启定时任务"+scheduledJob.getJobKey());
78 //停止
79 this.stop(scheduledJob.getJobKey());
80
81 return this.start(scheduledJob);
82 }
83
84 /**
85 * 执行启动任务
86 */
87 public void doStartTask(ScheduledJob sj){
88 log.info(sj.getJobKey());
89 if(sj.getStatus().intValue() != 1)
90 return;
91 Class<?> clazz;
92 ScheduledOfTask task;
93 try {
94 clazz = Class.forName(sj.getJobKey());
95 task = (ScheduledOfTask) SpringContextUtil.getBean(clazz);
96 } catch (ClassNotFoundException e) {
97 throw new IllegalArgumentException("spring_scheduled_cron表数据" + sj.getJobKey() + "有误", e);
98 }
99 Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口");
100 ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(task,(triggerContext -> new CronTrigger(sj.getCronExpression()).nextExecutionTime(triggerContext)));
101 scheduledFutureMap.put(sj.getJobKey(),scheduledFuture);
102 }
103
104 @Override
105 public void initTask() {
106 List<ScheduledJob> list = scheduledJobService.list();
107 for (ScheduledJob sj : list) {
108 if(sj.getStatus().intValue() == -1) //未启用
109 continue;
110 doStartTask(sj);
111 }
112 }
113 }

2.8 上面用到的获取Bean的工具类SpringContextUtil

 1 @Component
2 public class SpringContextUtil implements ApplicationContextAware {
3
4
5 private static ApplicationContext applicationContext = null;
6 @Override
7 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
8 if(SpringContextUtil.applicationContext == null){
9 SpringContextUtil.applicationContext = applicationContext;
10 }
11 }
12
13 public static ApplicationContext getApplicationContext() {
14 return applicationContext;
15 }
16
17 public static Object getBean(String name){
18 return getApplicationContext().getBean(name);
19 }
20
21 public static <T> T getBean(Class<T> clazz){
22 return getApplicationContext().getBean(clazz);
23 }
24
25 public static <T> T getBean(String name,Class<T> clazz){
26 return getApplicationContext().getBean(name, clazz);
27 }
28 }

2.9 表操作对应的一些类

Pojo

 1 @Data
2 @TableName("scheduled_job")
3 public class ScheduledJob {
4
5 @TableId(value = "job_id",type = IdType.AUTO)
6 private Integer jobId;
7
8 private String jobKey;
9
10 private String cronExpression;
11
12 private String taskExplain;
13
14 private Integer status;
15
16 }

ScheduledJobMapper

1 public interface ScheduledJobMapper extends BaseMapper<ScheduledJob> {
2 }

ScheduledJobService

1 public interface ScheduledJobService extends IService<ScheduledJob> {
2
3 /**
4 * 修改定时任务,并重新启动
5 * @param scheduledJob
6 * @return
7 */
8 boolean updateOne(ScheduledJob scheduledJob);
9 }
 1 @Service
2 @Slf4j
3 public class ScheduledJobServiceImpl extends ServiceImpl<ScheduledJobMapper, ScheduledJob> implements ScheduledJobService{
4
5 @Autowired
6 private ScheduledTaskService scheduledTaskService;
7
8 @Override
9 public boolean updateOne(ScheduledJob scheduledJob) {
10 if(updateById(scheduledJob))
11 scheduledTaskService.restart(getById(scheduledJob.getJobId()));
12 return true;
13 }
14 }

2.10 修改定时任务的接口

 1 @RestController
2 @RequestMapping("/job")
3 public class ScheduledJobController {
4
5 @Autowired
6 private ScheduledJobService scheduledJobService;
7
8 @PostMapping(value = "/update")
9 public CallBackResult update(HttpServletRequest request, ScheduledJob scheduledJob){
10 if(scheduledJobService.updateOne(scheduledJob))
11 return new CallBackResult(true,"修改成功");
12 return new CallBackResult(false,"修改失败");
13 }
14
15 }

3、测试结果

3.1 启动项目,看下定时任务的执行结果,控制台输出结果

我们可以看到任务1是每5秒执行一次,任务2是12秒执行一次

3.2 修改任务1的cron参数或者状态

3.2.1 修改cron,执行周期改为20秒执行一次,状态不变

再看控制台输出结果,任务2没变化,任务1由5秒一次变成了20秒一次了

3.2.1 修改状态

再看控制台输出结果,任务2没变化,任务1已经不再执行了

最后

第二种方式支持通过接口的方式去改动,并且不需要重启,当然啦,也可以直接在数据库中添加或修改数据后重启项目,配置更加灵活一点。

如果是一个固定的需求,执行周期一定不会变的了,推荐还是第一种写法,毕竟简单嘛。

如果觉得写得还不错的话,给个推荐鼓励一下吧。

SpringBoot如何实现定时任务的更多相关文章

  1. SpringBoot几种定时任务的实现方式

    定时任务实现的几种方式: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.使用这种方式可以让你的程序按照某一个频度执行, ...

  2. springBoot中的定时任务

    springBoot中的定时任务 1:在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置 2:新建ScheduledTasks任务类 : package c ...

  3. SpringBoot中的定时任务与Quartz的整合

    SpringBoot集成Quartz 定时任务Quartz : 就是在指定的时间执行一次或者循环执行,在项目的开发中有时候会需要的, 还是很有用的. SpringBoot内置的定时 添加依赖 < ...

  4. SpringBoot中执行定时任务

    一:在SpringBoot中使用定时任务相当的简单.首先,我们在启动类中加入@EnableScheduling来开启定时任务. @SpringBootApplication @EnableSchedu ...

  5. springboot整合@Scheduled定时任务的使用

    1.启动类里面添加注解@EnableScheduling ,例如: @SpringBootApplication@EnableScheduling@MapperScan("com.examp ...

  6. SpringBoot系列——动态定时任务

    前言 定时器是我们项目中经常会用到的,SpringBoot使用@Scheduled注解可以快速启用一个简单的定时器(详情请看我们之前的博客<SpringBoot系列--定时器>),然而这种 ...

  7. Springboot的默认定时任务——Scheduled注解

    本博客参考博文地址. 1.pom依赖: 引入springboot starter包即可 <dependencies> <dependency> <groupId>o ...

  8. 在SpringBoot中配置定时任务

    前言 之前在spring中使用过定时任务,使用注解的方式配置很方便,在SpringBoot中的配置基本相同,只是原来在spring中的xml文件的一些配置需要改变,在SpringBoot中也非常简单. ...

  9. springBoot中使用定时任务

    简单示例 导入依赖 springBoot已经默认集成了定时任务的依赖,只需要引入基本的依赖就可以使用定时任务. <parent> <groupId>org.springfram ...

  10. SpringBoot中并发定时任务的实现、动态定时任务的实现(看这一篇就够了)

    原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10659045.html,否则将追究法律责任!!! 一.在JAVA开发领域,目前可以通过以下 ...

随机推荐

  1. MySQL 编译安装并且开启DEBUG模式

    因为想分析下mysql中一些操作的内部执行过程,单纯的看源码太枯燥了,所以决定结合mysql的执行过程来分析,mysql作为一款成熟的数据库软件,在设计的时候就考虑到了调试的问题,只是想开启调试模式的 ...

  2. .net版高斯模糊算法

    最近挺多人找高斯算法,本人贴上一个高斯模糊算法类,希望可以帮助到大家.算法的效率还是可以接受的. #region 高斯模糊算法 /// <summary> /// 高斯模糊算法 /// & ...

  3. BestCoder Round #87 1001

    GCD is Funny Accepts: 524 Submissions: 1147 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 655 ...

  4. Contoso 大学 - 2 – 实现基本的增删改查

    原文 Contoso 大学 - 2 – 实现基本的增删改查 目录 Contoso 大学 - 使用 EF Code First 创建 MVC 应用 原文地址:http://www.asp.net/mvc ...

  5. 我的创业劲儿,无可阻挡-JAVA学院张孝伟

    导语:张孝伟,这个怀揣着创业梦想的农村小伙,为了报答父母的恩情,他开启了自己的逐梦之旅.友好的伙伴,火旺的生意.以前让他如鱼得水.就在他满足于现状的时候,一场突如其来的事故,让他一夜间倾家荡产.他是否 ...

  6. 【Netty】Netty之Bootstrapping

    一.前言 前面已经学习了Netty的EventLoop以及线程模型,接着学习Netty的Bootstrapping. 二.Bootstrapping 在学习了Netty中的很多组件后,如何将这些组件有 ...

  7. ASP.NET Core 从 gitlab-ci 环境变量读取配置

    最近在加强持续集成,遇到一个场景需要通过 gitlab-ci 环境变量(Settings -> Settings -> CI/CD -> Variables )在持续集成时向 ASP ...

  8. lambda Helper

    /// <summary> /// 操作表达式共通类,条件并且,或者操作等 /// </summary> public static class PredicateBuilde ...

  9. break与continue关键字的使用

    break与continue关键字的使用break:使用在switch-case中或者循环中 如果使用在循环中,表示:结束当前循环 public class V{ public static void ...

  10. MAT eclipse内存分析工具

      启动的时候提示: Failed to load the JNIshared library 解决办法: 查看配置文件:MemoryAnalyzer.ini --launcher.librarypl ...