基于数据库动态配置的定时任务
1 基于反射的定时任务创建
1.1 pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-maven-plugin -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.6.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<!--test case use-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
1.2 数据库表的创建
DROP TABLE IF EXISTS `SYS_TIMERS_TASK`;
CREATE TABLE `SYS_TIMERS_TASK` (
`id` bigint(20) not null comment '定时任务id',
`timer_name` varchar(255) null default null comment '任务名称',
`action_class` varchar(255) null default null comment '执行定时任务的class全类名',
`cron` varchar(255) null default '' comment '定时任务表达式',
`job_status` tinyint(4) null default 2 comment '状态字典 1运行 2停止',
`remark` varchar(1000) null default null comment '备注信息',
`create_time` timestamp null default null comment '创建时间',
`update_time` timestamp null default now() comment '更新时间',
`applicable_ip` varchar(1000) null default '' comment '适用于这个任务的IP地址,逗号间隔,空为都适用',
primary key (`id`)
) default charset = utf8mb4 collate = utf8mb4_bin comment = '定时任务';
insert into SYS_TIMERS_TASK VALUES (1,"test","com.sunshine.timerTask.TimerTaskTest","0 */1 * * * ?",1,"test",now(),now(),null);
insert into SYS_TIMERS_TASK VALUES (2,"test1","com.sunshine.timerTask.TimerTaskTest1","0 */2 * * * ?",1,"test1",now(),now(),null);
1.3 对应实体类的创建
package com.sunshine.timerTask;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @auther July
* @create 2021-06-29
* @desc ...
*/
@Data
@Entity
@Table(name = "SYS_TIMERS_TASK")
public class SysTimersTask {
@Id()
private Long id;
//任务名称
private String timerName;
//执行任务的class类名
private String actionClass;
//定时任务表达式
private String cron;
//状态(字典 1运行 2停止)
private Integer jobStatus;
//备注信息
private String remark;
//适用于这个任务的IP地址,多个地址以逗号分隔
private String applicableIp;
}
1.4 提供操作数据库表中数据的repository
package com.sunshine.timerTask;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @auther July
* @create 2021-06-29
* @desc ...
*/
@Repository
public interface SysTimersTaskRepository extends JpaRepository<SysTimersTask, Long> {
List<SysTimersTask> findByJobStatus(int jobStatus);
}
1.5 创建操作表中数据的service
package com.sunshine.timerTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* @auther July
* @create 2021-06-29
* @desc ...
*/
@Service
@Slf4j
public class SysTimersTaskService {
@Autowired
private SysTimersTaskRepository sysTimersTaskRepository;
public List<SysTimersTask> getAllTask() {
return sysTimersTaskRepository.findAll();
}
public SysTimersTask findById(Long id) {
Optional<SysTimersTask> byId = sysTimersTaskRepository.findById(id);
if (byId.isPresent()) {
return byId.get();
} else {
return null;
}
}
public List<SysTimersTask> getByJobStatus(int jobStatus) {
return sysTimersTaskRepository.findByJobStatus(jobStatus);
}
public void addTask(SysTimersTask sysTimersTask) {
sysTimersTaskRepository.save(sysTimersTask);
}
public void deleteTask(Long id) {
sysTimersTaskRepository.deleteById(id);
}
public void updateTask(SysTimersTask sysTimersTask) {
sysTimersTaskRepository.save(sysTimersTask);
}
}
1.6 定时任务需要执行的方法
1.6.1 接口
package com.sunshine.timerTask;
/**
* @auther July
* @create 2021-07-06
* @desc ...
*/
public interface TimerTaskRunner {
void action();
}
1.6.2 接口的实现类,可以有多个
第一个:
package com.sunshine.timerTask;
import org.springframework.stereotype.Component;
/**
* @auther July
* @create 2021-07-06
* @desc ...
*/
@Component
public class TimerTaskTest implements TimerTaskRunner {
@Override
public void action() {
System.out.println("timer task test!!!");
}
}
第二个:
package com.sunshine.timerTask;
import org.springframework.stereotype.Component;
/**
* @auther July
* @create 2021-07-06
* @desc ...
*/
@Component
public class TimerTaskTest1 implements TimerTaskRunner {
@Override
public void action() {
System.out.println("timer task test@@@");
}
}
1.7 定时任务主体
package com.sunshine.timerTask;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.ObjectUtils;
import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
/**
* @auther July
* @create 2021-07-06
* @desc 主要使用反射来完成最后一步的
*/
@Slf4j
//@EnableScheduling
//@Service
public class TimerTaskService implements SchedulingConfigurer {
@Autowired
private SysTimersTaskRepository sysTimersTaskRepository;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
List<SysTimersTask> timersTasks = sysTimersTaskRepository.findAll();
for (SysTimersTask timersTask : timersTasks) {
if (ObjectUtils.isEmpty(timersTask)) {
log.error("the parameter is error:{}", timersTask);
return;
}
//预加载该类看是否存在
try {
Class.forName(timersTask.getActionClass());
} catch (ClassNotFoundException e) {
log.error("the timer task not exist,{}", e.getMessage());
return;
}
//判断本机的IP地址是否可以使用这个定时任务
if (timersTask.getApplicableIp() != null) {
try {
String[] applications = timersTask.getApplicableIp().split(",");
InetAddress localHost = Inet4Address.getLocalHost();
String hostAddress = localHost.getHostAddress();
boolean isContain = ArrayUtils.contains(applications, hostAddress);
if (!isContain) {
log.info("the host address is not in the applications");
return;
}
} catch (UnknownHostException e) {
log.error("get localHost failed");
}
}
try {
Class targetClass = Class.forName(timersTask.getActionClass());
Method[] methods = targetClass.getMethods();
scheduledTaskRegistrar.addTriggerTask(
//定时任务要执行的内容(runnable)
()-> {
for (Method method : methods) {
if ("action".equals(method.getName())) {
try {
method.invoke(targetClass.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
},
//设置执行周期(trigger)
triggerContext -> {
//从数据库获取执行周期
String cron = timersTask.getCron();
//校验合法性
if (org.apache.commons.lang3.StringUtils.isEmpty(cron)) {
log.info("corn is empty!");
}
//返回执行周期
return new CronTrigger(cron).nextExecutionTime(triggerContext);
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2 基于Hutool插件的定时任务的创建
2.1 定时任务主体
package com.sunshine.timerTask;
import cn.hutool.cron.CronUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @auther July
* @create 2021-06-29
* @desc 采用hutool来遍历数据库表中的定时任务
*/
@Component
public class TimerTaskRunListener implements ApplicationListener<ApplicationReadyEvent>, Ordered {
@Autowired
private SysTimersTaskService timersTaskService;
@Autowired
private HutoolTimerTaskService hutoolTimerTaskService;
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
//添加定时任务到调度器
List<SysTimersTask> timersTasks = timersTaskService.getByJobStatus(1);
for (SysTimersTask timersTask : timersTasks) {
hutoolTimerTaskService.startTimerTask(timersTask);
}
//启用级别设置为秒级
CronUtil.setMatchSecond(true);
//启动定时器执行器
CronUtil.start();
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE;
}
}
2.2 定时任务处理逻辑
package com.sunshine.timerTask;
import cn.hutool.cron.CronUtil;
import cn.hutool.cron.task.Task;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @auther July
* @create 2021-07-06
* @desc Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,
* 提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
*/
@Slf4j
@Service
public class HutoolTimerTaskService {
public void startTimerTask(SysTimersTask timersTask) {
if (ObjectUtils.isEmpty(timersTask)) {
log.error("the parameter is error:{}", timersTask);
return;
}
//预加载该类看是否存在
try {
Class.forName(timersTask.getActionClass());
} catch (ClassNotFoundException e) {
log.error("the timer task not exist,{}", e.getMessage());
return;
}
//判断本机的IP地址是否可以使用这个定时任务
if (timersTask.getApplicableIp() != null) {
try {
String[] applications = timersTask.getApplicableIp().split(",");
InetAddress localHost = Inet4Address.getLocalHost();
String hostAddress = localHost.getHostAddress();
boolean isContain = ArrayUtils.contains(applications, hostAddress);
if (!isContain) {
log.info("the host address is not in the applications");
return;
}
} catch (UnknownHostException e) {
log.error("get localHost failed");
}
}
Task task = () -> {
try {
TimerTaskRunner timerTaskRunner = (TimerTaskRunner) SpringUtil.getBean(Class.forName(timersTask.getActionClass()));
timerTaskRunner.action();
} catch (Exception e) {
e.printStackTrace();
}
};
CronUtil.schedule(String.valueOf(timersTask.getId()),timersTask.getCron(),task);
}
}