基于数据库动态配置的定时任务

本文介绍了如何使用Spring Boot结合Hutool创建基于数据库的定时任务,包括添加依赖、数据库表设计、实体类创建、Repository与Service接口实现,以及利用反射实现定时任务执行。同时展示了基于Hutool插件的定时任务创建过程。
摘要由CSDN通过智能技术生成

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);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值