引用地址:https://blog.csdn.net/qq_14861089/article/details/108831101
功能实现:
1.实体类:
package com.mengan.utilsProject.task;
import lombok.Data;
@Data
public class Scheduled {
private int id;
private String taskKey;
private String name;
private String cron;
private int status;
private String createTime;
private String updateTime;
}
2.Mapper接口类:
package com.mengan.utilsProject.task;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
//@Repository
public interface ScheduledMapper {
List<Scheduled> selectList(Scheduled scheduled);
}
3.Mapper接口对应的xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mengan.utilsProject.task.ScheduledMapper">
<select id="selectList" resultType="com.mengan.utilsProject.task.Scheduled">
select * from scheduled where status = 1
<if test="taskKey != null and taskKey != ''">
AND task_key = #{taskKey}
</if>
</select>
</mapper>
4.状态枚举类
package com.mengan.utilsProject.task;
public enum ScheduledStatus {
ENABLE(0, "禁用"),
DISABLE(1, "启用");
private int code;
private String name;
ScheduledStatus(int code, String name){
this.code = code;
this.name = name;
}
public static ScheduledStatus getByCode(int code){
for (ScheduledStatus st : values()) {
if(code == st.getCode())
return st;
}
return null;
}
public int getCode() {
return code;
}
public String getName() {
return name;
}
}
5.配置类
package com.mengan.utilsProject.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@Slf4j
@Configuration
public class ScheduledTaskConfig {
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
log.info("创建定时任务调度线程池 start");
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(20);
threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
//用来设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean,这样这些异步任务的销毁就会先于Redis线程池的销毁。
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
//该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
log.info("创建定时任务调度线程池 end");
return threadPoolTaskScheduler;
}
}
6.自定义定时任务接口
package com.mengan.utilsProject.task;
/**
* @ClassName ScheduledTaskJob
* @Description 创建调度任务公共父接口
* @Author
* @Date
* @Version
*/
public interface ScheduledTaskJob extends Runnable{
}
7.自定义定时任务实现类1
package com.mengan.utilsProject.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @ClassName ScheduledTaskJob01
* @Description 测试类01
* @Author
* @Date 2022/01/26
* @Version 1.0
*/
@Slf4j
@Service
public class ScheduledTaskJob01 implements ScheduledTaskJob {
@Override
public void run() {
// TODO 要处理的业务逻辑
log.info("ScheduledTask => 01 run 当前线程名称 {} ", Thread.currentThread().getName());
}
}
8.自定义定时任务实现类2
package com.mengan.utilsProject.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @ClassName ScheduledTaskJob02
* @Description 测试类02
* @Author
* @Date 2022/01/26
* @Version 1.0
*/
@Slf4j
@Service
public class ScheduledTaskJob02 implements ScheduledTaskJob {
@Override
public void run() {
// TODO 要处理的业务逻辑
log.info("ScheduledTask => 02 run 当前线程名称 {} ", Thread.currentThread().getName());
}
}
9.自定义定时任务实现类3
package com.mengan.utilsProject.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @ClassName ScheduledTaskJob03
* @Description 测试类03
* @Author
* @Date 2022/01/26
* @Version 1.0
*/
@Slf4j
@Service
public class ScheduledTaskJob03 implements ScheduledTaskJob {
@Override
public void run() {
// TODO 要处理的业务逻辑
log.info("ScheduledTask => 03 run 当前线程名称 {} ", Thread.currentThread().getName());
}
}
10.ScheduledTaskRunner类:
package com.mengan.utilsProject.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ScheduledTaskRunner implements ApplicationRunner {
@Autowired
private ScheduledTaskService scheduledTaskService;
/**
* 程序启动完毕后,需要自启的任务
*/
@Override
public void run(ApplicationArguments applicationArguments){
log.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 开始!");
scheduledTaskService.initAllTask();
log.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 结束!");
}
}
11.ScheduledTaskService接口
package com.mengan.utilsProject.task;
/**
* @ClassName ScheduledTaskService
* @Description 定时任务接口
* @Author
* @Date
* @Version 1.0
*/
public interface ScheduledTaskService {
/**
* 根据任务key 启动任务
*/
Boolean start(String taskKey, Scheduled scheduled);
/**
* 根据任务key 停止任务
*/
Boolean stop(String taskKey);
/**
* 根据任务key 重启任务
*/
Boolean restart(String taskKey, Scheduled scheduled);
/**
* 初始化 ==> 启动所有正常状态的任务
*/
void initAllTask();
}
12.定时任务实现类
package com.mengan.utilsProject.task;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName ScheduledTaskServiceImpl
* @Description 定时任务实现
* @Author
* @Date
* @Version 1.0
*/
@Slf4j
@Service
public class ScheduledTaskServiceImpl implements ScheduledTaskService {
@Value("${task.enabled}")
private Boolean taskEnable;
/**
* 可重入锁
*/
private ReentrantLock lock = new ReentrantLock();
/**
* 定时任务线程池
*/
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
/**
* 存放已经启动的任务map
*/
private Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();
@Autowired
private ScheduledMapper scheduledMapper;
/**
* 描述: 根据任务key 启动任务
*
* @param taskKey
* @param scheduled
* @return java.lang.Boolean
* @author lv617
* @date 2020/9/24 11:16
*/
@Override
public Boolean start(String taskKey, Scheduled scheduled) {
log.info(">>>>>> 启动任务 {} 开始 >>>>>>", taskKey);
//添加锁放一个线程启动,防止多人启动多次
lock.lock();
log.info(">>>>>> 添加任务启动锁完毕");
try {
//校验是否已经启动
if (this.isStart(taskKey)) {
log.info(">>>>>> 当前任务已经启动,无需重复启动!");
return false;
}
//查询配置
if(scheduled == null)
scheduled = this.getByTaskKey(taskKey);
if(scheduled == null)
return false;
//启动任务
this.doStartTask(scheduled);
} finally {
// 释放锁
lock.unlock();
log.info(">>>>>> 释放任务启动锁完毕");
}
log.info(">>>>>> 启动任务 {} 结束 >>>>>>", taskKey);
return true;
}
/**
* 描述: 查询定时任务配置参数
*
* @param taskKey
* @return com.yihaocard.main.module.scheduled.model.Scheduled
* @author lv617
* @date 2020/9/24 11:14
*/
private Scheduled getByTaskKey(String taskKey) {
Scheduled scheduledExample = new Scheduled();
scheduledExample.setStatus(1);
scheduledExample.setTaskKey(taskKey);
List<Scheduled> scheduleds = scheduledMapper.selectList(scheduledExample);
if(scheduleds == null || scheduleds.size() < 1)
return null;
return scheduleds.get(0);
}
/**
* 描述: 根据 key 停止任务
*
* @param taskKey
* @return java.lang.Boolean
* @author lv617
* @date 2020/9/24 11:17
*/
@Override
public Boolean stop(String taskKey) {
log.info(">>>>>> 进入停止任务 {} >>>>>>", taskKey);
//当前任务实例是否存在
boolean taskStartFlag = scheduledFutureMap.containsKey(taskKey);
log.info(">>>>>> 当前任务实例是否存在 {}", taskStartFlag);
if (taskStartFlag) {
//获取任务实例
ScheduledFuture scheduledFuture = scheduledFutureMap.get(taskKey);
//关闭实例
boolean cancel = scheduledFuture.cancel(true);
log.info("cancel:{}", cancel);
//删除关闭的任务实例
scheduledFutureMap.remove(taskKey);
}
log.info(">>>>>> 结束停止任务 {} >>>>>>", taskKey);
return taskStartFlag;
}
/**
* 描述: 根据任务key 重启任务
*
* @param taskKey
* @param scheduled
* @return java.lang.Boolean
* @author lv617
* @date 2020/9/24 11:18
*/
@Override
public Boolean restart(String taskKey, Scheduled scheduled) {
log.info(">>>>>> 进入重启任务 {} >>>>>>", taskKey);
//先停止
this.stop(taskKey);
//查询配置
if(scheduled == null)
scheduled = this.getByTaskKey(taskKey);
if(scheduled == null)
return false;
//再启动
return this.start(taskKey,scheduled);
}
/**
* 初始化 ==> 启动所有正常状态的任务
*/
@Override
public void initAllTask() {
if(!taskEnable){
log.info("配置文件禁用了定时任务----");
return;
}
Scheduled scheduledExample = new Scheduled();
scheduledExample.setStatus(1);
List<Scheduled> scheduleds = scheduledMapper.selectList(scheduledExample);
log.info("初始化 ==> 启动所有正常状态的任务开始 !size={}", scheduleds == null ? 0 : scheduleds.size());
if (scheduleds == null || scheduleds.size() < 1) {
return;
}
for (Scheduled scheduled : scheduleds) {
//任务 key
String taskKey = scheduled.getTaskKey();
//校验是否已经启动
if (this.isStart(taskKey)) {
// 重启任务
this.restart(taskKey,scheduled);
} else {
// 启动任务
this.doStartTask(scheduled);
}
}
log.info("初始化 ==> 启动所有正常状态的任务结束 !");
}
/**
* 执行启动任务
*/
private void doStartTask(Scheduled scheduled) {
if (scheduled == null)
return;
//任务key
String taskKey = scheduled.getTaskKey();
//定时表达式
String taskCron = scheduled.getCron();
//获取需要定时调度的接口
ScheduledTaskJob scheduledTaskJob = (ScheduledTaskJob) SpringUtil.getBean(taskKey);
log.info(">>>>>> 任务 [ {} ] ,cron={}", scheduled.getName(), taskCron);
ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduledTaskJob, (TriggerContext triggerContext) -> new CronTrigger(taskCron).nextExecutionTime(triggerContext));
//将启动的任务放入 map
scheduledFutureMap.put(taskKey, scheduledFuture);
}
/**
* 任务是否已经启动
*/
private Boolean isStart(String taskKey) {
//校验是否已经启动
if (scheduledFutureMap.containsKey(taskKey)) {
if (!scheduledFutureMap.get(taskKey).isCancelled()) {
return true;
}
}
return false;
}
}
13.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mengan.utilsProject.task.ScheduledMapper">
<select id="selectList" resultType="com.mengan.utilsProject.task.Scheduled">
select * from scheduled where status = 1
<if test="taskKey != null and taskKey != ''">
AND task_key = #{taskKey}
</if>
</select>
</mapper>
14.application.yml文件
server:
port: 38080
task:
enabled: 1
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.jdbc.Driver
mybatis:
mapper-locations: classpath:mapping/*Mapper.xml
type-aliases-package: com.mengan.utilsProject.task
configuration:
map-underscore-to-camel-case: true
#showSql
logging:
level:
com:
example:
mapper : debug
15.数据库表:
CREATE TABLE `scheduled` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`task_key` varchar(127) NOT NULL COMMENT '任务key值(使用bean名称)',
`name` varchar(127) DEFAULT NULL COMMENT '任务名称',
`cron` varchar(63) NOT NULL COMMENT '任务表达式',
`status` int(2) DEFAULT '0' COMMENT '状态(0.禁用; 1.启用)',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uniqu_task_key` (`task_key`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务配置表';