SpringBoot如何实现定时任务
写在前面
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如何实现定时任务的更多相关文章
- SpringBoot几种定时任务的实现方式
定时任务实现的几种方式: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.使用这种方式可以让你的程序按照某一个频度执行, ...
- springBoot中的定时任务
springBoot中的定时任务 1:在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置 2:新建ScheduledTasks任务类 : package c ...
- SpringBoot中的定时任务与Quartz的整合
SpringBoot集成Quartz 定时任务Quartz : 就是在指定的时间执行一次或者循环执行,在项目的开发中有时候会需要的, 还是很有用的. SpringBoot内置的定时 添加依赖 < ...
- SpringBoot中执行定时任务
一:在SpringBoot中使用定时任务相当的简单.首先,我们在启动类中加入@EnableScheduling来开启定时任务. @SpringBootApplication @EnableSchedu ...
- springboot整合@Scheduled定时任务的使用
1.启动类里面添加注解@EnableScheduling ,例如: @SpringBootApplication@EnableScheduling@MapperScan("com.examp ...
- SpringBoot系列——动态定时任务
前言 定时器是我们项目中经常会用到的,SpringBoot使用@Scheduled注解可以快速启用一个简单的定时器(详情请看我们之前的博客<SpringBoot系列--定时器>),然而这种 ...
- Springboot的默认定时任务——Scheduled注解
本博客参考博文地址. 1.pom依赖: 引入springboot starter包即可 <dependencies> <dependency> <groupId>o ...
- 在SpringBoot中配置定时任务
前言 之前在spring中使用过定时任务,使用注解的方式配置很方便,在SpringBoot中的配置基本相同,只是原来在spring中的xml文件的一些配置需要改变,在SpringBoot中也非常简单. ...
- springBoot中使用定时任务
简单示例 导入依赖 springBoot已经默认集成了定时任务的依赖,只需要引入基本的依赖就可以使用定时任务. <parent> <groupId>org.springfram ...
- SpringBoot中并发定时任务的实现、动态定时任务的实现(看这一篇就够了)
原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10659045.html,否则将追究法律责任!!! 一.在JAVA开发领域,目前可以通过以下 ...
随机推荐
- JAVA中直接用Jdbc就能操作数据库了,为什么还要用spring框架?
不过随着业务的扩展,你就会发现jdbc建立一个连接居然要几百毫秒,而执行一个普通的SQL仅仅需要几毫秒. 这么重量级的资源建立了就释放了不合适,得找个容器存起来,谁要就来取,不用了就还给容器,毕竟容器 ...
- 修改Windows7系统默认软件安装目录
Windows7系统默认软件安装目录都在C盘Program Files文件夹有时候我们需要把软件安装到其他地方,如果每次安装的时候都要重新选择一次十分麻烦,下面Windows7之家教你修改软件默认安装 ...
- Java特性和优势
Java特性和优势 简单性 面向对象性 可移植性 高性能 分布式 动态性 多线程 安全性 健壮性
- 源码解析Spring AOP的加载与生效
本次博主主要进行Spring AOP这里的解析,因为在工作中使用后,却不知道背后的实现原理并在使用的过程中发现了一些认知缺陷,所以决定写这么一篇文章以供大家参考参考,进入正题. 本次博主使用了@Asp ...
- JS_DOM操作之查找标签
1 - 直接查找标签 // 方式1:获取元素 document.getElementsByTagName("标签名") document.getElementById(" ...
- jq的选择器中带有特殊符号无法获取元素
因项目需要,将元素id命名为数组(array[i].string) 使用jq去获取该id的元素时,返回的是个undefined.即jq获取不到该元素,因为该元素中的id含有特殊字符"[&qu ...
- JS001. antd vue遍历setFieldsValue表单键值对无效 ( {} -> new Object() )
问题代码: const tempFieldsValue = this.form.getFieldsValue() Object.keys(tempFieldsValue).map((k) => ...
- 使用dubbo-go搭建dubbo接口测试平台
背景 http接口测试只需要一个curl命令,但dubbo协议没有这样的现成接口测试工具.通常公司内的dubbo控制台或其他平台会集成一个dubbo接口测试工具. 调用一个dubbo接口,需要知道服务 ...
- python库--flashtext--大规模数据清洗利器
flashtext.keyword (flashtext) 类/方法 返回值 参数 说明 .KeywordProcessor() 对象kp case_sensitive=False 是否区分大小写 添 ...
- 如何高效掌控K8s资源变化?K8s Informer实现机制浅析
作者 王成,腾讯云研发工程师,Kubernetes contributor,从事数据库产品容器化.资源管控等工作,关注 Kubernetes.Go.云原生领域. 概述 进入 K8s 的世界,会发现有很 ...