SpringBoot 自动化定时任务管理

SpringBoot 自动化定时任务管理

最近公司中写定时任务相关的东西,但是呢本人觉的不通用,就写了一个通过数据库配置可以自动关闭/打开/修改调度时间的自动化定时任务;
二话不说代码如下:

定时线程池

package com.itechhero.app.module.config.scheduler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
@Slf4j
public class SchedulerConfig {

    @Bean
    public TaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        /*定时任务执行线程池核心线程数*/
        taskScheduler.setPoolSize(20);
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setThreadNamePrefix("TechHero-Scheduler-");
        return taskScheduler;
    }

}

定时ApplicationContext 工具类

package com.itechhero.app.module.config;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * 获取ApplicationContextG工具类
 * 作者: 吴 波
 * 时间: 2020-03-17 19:57
 * 笔名: 那年秋天的茄子^^
 */
@Component
public class AppContextUtils implements ApplicationContextAware {

    public static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context=applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }

}

定时管理器

package com.itechhero.app.module.config.scheduler;

import com.itechhero.app.module.common.utils.req.ResBean;
import com.itechhero.app.module.config.AppContextUtils;
import com.itechhero.app.module.edu.timer.model.TimerBean;
import com.itechhero.app.module.edu.timer.service.TimerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 自动化定时任务管理器
 * 作者: 吴 波
 * 时间: 2020-03-26 17:21
 * 笔名: 那年秋天的茄子^^
 */
@Slf4j
@Component
public class SchedulerManager {

    @Autowired
    private TaskScheduler threadPoolTaskScheduler;

    @Autowired
    private TimerService timerService;

    /*定义定时列表集合*/
    private ConcurrentHashMap<String, TimerEntity> timers = new ConcurrentHashMap<>();

    @PostConstruct
    private void run(){
        /*启动运行,先保存定时*/
        createTimers();
        ConcurrentHashMap.KeySetView<String, TimerEntity> keySet = timers.keySet();
        for (String key : keySet) {
            TimerEntity timerEntity = timers.get(key);
            if(timerEntity.isStart()){
                startTimer(timerEntity);
            }
        }
    }

    /**
     * 启动扫描所有定时任务
     * 作者: 吴 波
     * 时间: 2020-03-26 18:43
     * 笔名: 那年秋天的茄子^^
     */
    private void createTimers() {
        List<TimerBean> timerBeans = timerService.getAll();
        for (TimerBean timerBean : timerBeans) {
            TimerEntity timerEntity=new TimerEntity();
            timerEntity.setTimerBean(timerBean);
            timers.put(timerBean.getId(), timerEntity);
        }
    }

    /**
     * 启动定时
     * 作者: 吴 波
     * 时间: 2020-03-26 19:16
     * 笔名: 那年秋天的茄子^^
     */
    public ResBean startTimer(TimerEntity timerEntity) {

        /*获取容器中的Timer*/
        TimerEntity timer = this.timers.get(timerEntity.getId());

        /*如果容器中有对应的Timer并且具有执行的任务则释放任务*/
        if(timer!=null && timer.getFuture()!=null){
            timer.getFuture().cancel(true);
        }

        /*查看当前容器中没有,并且当前任务不需要执行,之间返回*/
        if(timer!=null && !timerEntity.isStart()){
            this.timers.remove(timerEntity.getId());
            return ResBean.success();
        }

        final String className=timerEntity.getClassName();
        final String methodName=timerEntity.getMethodName();
        final String timerName=timerEntity.getTimerName();
        final String cron=timerEntity.getCron();

        ScheduledFuture<?> future ;

        AtomicBoolean      flag   = new AtomicBoolean(true);
        future = threadPoolTaskScheduler.schedule(() -> {
            try {
                Object timerObjBean = AppContextUtils.getContext().getBean(className);
                Method runMethod    = timerObjBean.getClass().getMethod(methodName);
                if (Modifier.isPublic(runMethod.getModifiers())) {
                    long startTime = System.currentTimeMillis();
                    log.info("\n[定时任务-{}]:[正在运行...]",timerName);
                    try {
                        runMethod.invoke(timerObjBean);
                    } catch (Exception e) {
                       log.error("\n[定时任务-{}]:[运行出现异常..]",timerName,e);
                    }
                    long endTime = System.currentTimeMillis();
                    log.info("\n[定时任务-{}]-[运行结束...]\n[消耗时间={}毫秒]",timerName,endTime-startTime);
                } else {
                    flag.set(false);
                }
            } catch (Exception e) {
                log.error("创建定时任务失败", e);
                flag.set(false);
            }
        }, triggerContext -> {
            if (StringUtils.isBlank(cron)) {
                return null;
            }
            /*定时任务触发,可修改定时任务的执行周期*/
            CronTrigger trigger = new CronTrigger(cron);
            return trigger.nextExecutionTime(triggerContext);
        });

        /*如果创建失败,则清除当前执行线程,并且将当前定时线程排除*/
        if(!flag.get()){
            if(future!=null){
                future.cancel(true);
            }
            return ResBean.failed("定时创建出现问题!");
        }

        timerEntity.setFuture(future);
        this.timers.put(timerEntity.getId(),timerEntity);

        log.info("\n[自动化定时任务:\n{}-{}-启动成功...]",timerName,cron);
        return ResBean.success();
    }
}

定时实体 TimerEntity

package com.itechhero.app.module.config.scheduler;

import com.itechhero.app.module.edu.timer.model.TimerBean;
import lombok.Data;

import java.io.Serializable;
import java.util.concurrent.ScheduledFuture;

@Data
public class TimerEntity implements Serializable {

    private TimerBean timerBean;

    private ScheduledFuture<?> future;


    public ScheduledFuture<?> getFuture() {
        return future;
    }

    public void setFuture(ScheduledFuture<?> future) {
        this.future = future;
    }


    public boolean isStart(){
        return this.timerBean.isStart();
    }

    /**
     * 获取唯一标识
     *
     * @return ID - 唯一标识
     */
    public String getId() {
        return this.timerBean.getId();
    }

    /**
     * 获取定时名称
     *
     * @return TIMER_NAME - 定时名称
     */
    public String getTimerName() {
        return this.timerBean.getTimerName();
    }

    /**
     * 获取描述
     *
     * @return DESCRIBE - 描述
     */
    public String getDescribe() {
        return this.timerBean.getDescribe();
    }

    /**
     * 获取服务类名
     *
     * @return CLASS_NAME - 服务类名
     */
    public String getClassName() {
        return this.timerBean.getClassName();
    }

    /**
     * 获取函数名称
     *
     * @return METHOD_NAME - 函数名称
     */
    public String getMethodName() {
        return this.timerBean.getMethodName();
    }

    /**
     * 获取定时表达式
     *
     * @return CRON - 定时表达式
     */
    public String getCron() {
        return this.timerBean.getCron();
    }
}

数据库实体 TimerBean

package com.itechhero.app.module.edu.timer.model;

import lombok.ToString;

import javax.persistence.*;
import java.io.Serializable;

@Table(name = "YJ_TIMER")
@ToString
public class TimerBean implements Serializable {
    /**
     * 唯一标识
     */
    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private String id;

    /**
     * 定时名称
     */
    @Column(name = "TIMER_NAME")
    private String timerName;

    /**
     * 描述
     */
    @Column(name = "DESCRIBE")
    private String describe;

    /**
     * 服务类名
     */
    @Column(name = "CLASS_NAME")
    private String className;

    /**
     * 函数名称
     */
    @Column(name = "METHOD_NAME")
    private String methodName;

    /**
     * 定时表达式
     */
    @Column(name = "CRON")
    private String cron;

    /**
     * 是否启用
     */
    @Column(name = "IF_START")
    private Integer ifStart;

    /**
     * 获取唯一标识
     *
     * @return ID - 唯一标识
     */
    public String getId() {
        return id;
    }

    /**
     * 设置唯一标识
     *
     * @param id 唯一标识
     */
    public void setId(String id) {
        this.id = id == null ? null : id.trim();
    }

    /**
     * 获取定时名称
     *
     * @return TIMER_NAME - 定时名称
     */
    public String getTimerName() {
        return timerName;
    }

    /**
     * 设置定时名称
     *
     * @param timerName 定时名称
     */
    public void setTimerName(String timerName) {
        this.timerName = timerName == null ? null : timerName.trim();
    }

    /**
     * 获取描述
     *
     * @return DESCRIBE - 描述
     */
    public String getDescribe() {
        return describe;
    }

    /**
     * 设置描述
     *
     * @param describe 描述
     */
    public void setDescribe(String describe) {
        this.describe = describe == null ? null : describe.trim();
    }

    /**
     * 获取服务类名
     *
     * @return CLASS_NAME - 服务类名
     */
    public String getClassName() {
        return className;
    }

    /**
     * 设置服务类名
     *
     * @param className 服务类名
     */
    public void setClassName(String className) {
        this.className = className == null ? null : className.trim();
    }

    /**
     * 获取函数名称
     *
     * @return METHOD_NAME - 函数名称
     */
    public String getMethodName() {
        return methodName;
    }

    /**
     * 设置函数名称
     *
     * @param methodName 函数名称
     */
    public void setMethodName(String methodName) {
        this.methodName = methodName == null ? null : methodName.trim();
    }

    /**
     * 获取定时表达式
     *
     * @return CRON - 定时表达式
     */
    public String getCron() {
        return cron;
    }

    /**
     * 设置定时表达式
     *
     * @param cron 定时表达式
     */
    public void setCron(String cron) {
        this.cron = cron == null ? null : cron.trim();
    }

    /**
     * 获取是否启用
     *
     * @return IF_START - 是否启用
     */
    public Integer getIfStart() {
        return ifStart;
    }

    /*工具方法*/
    public Boolean isStart(){
        return this.ifStart==1;
    }

    /**
     * 设置是否启用
     *
     * @param ifStart 是否启用
     */
    public void setIfStart(Integer ifStart) {
        this.ifStart = ifStart;
    }
}

定时Mapper

package com.itechhero.app.module.edu.timer.mapper;

import com.itechhero.app.module.common.utils.req.CMap;
import com.itechhero.app.module.config.mybatis.base.BaseMapper;
import com.itechhero.app.module.edu.timer.model.TimerBean;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface TimerBeanMapper extends BaseMapper<TimerBean> {
    /**
     * 获取分页定时任务
     * 作者: 吴 波
     * 时间: 2020-03-26 22:08
     * 笔名: 那年秋天的茄子^^
     */
    List<CMap> getTimers(@Param("param") CMap param);
}

定时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.itechhero.app.module.edu.timer.mapper.TimerBeanMapper">
  <resultMap id="BaseResultMap" type="com.itechhero.app.module.edu.timer.model.TimerBean">
    <id column="ID" jdbcType="VARCHAR" property="id" />
    <result column="TIMER_NAME" jdbcType="VARCHAR" property="timerName" />
    <result column="DESCRIBE" jdbcType="VARCHAR" property="describe" />
    <result column="CLASS_NAME" jdbcType="VARCHAR" property="className" />
    <result column="METHOD_NAME" jdbcType="VARCHAR" property="methodName" />
    <result column="CRON" jdbcType="VARCHAR" property="cron" />
    <result column="IF_START" jdbcType="DECIMAL" property="ifStart" />
  </resultMap>
    <select id="getTimers" resultType="com.itechhero.app.module.common.utils.req.CMap">
        select * from YJ_TIMER
        where 1 = 1
        <if test="param.className!=''">
            AND CLASS_NAME LIKE '%'||#{param.className}||'%'
        </if>
        <if test="param.methodName!=''">
            AND METHOD_NAME LIKE '%'||#{param.methodName}||'%'
        </if>
        <if test="param.timerName!=''">
            AND TIMER_NAME LIKE '%'||#{param.timerName}||'%'
        </if>

    </select>

</mapper>

定时Service

package com.itechhero.app.module.edu.timer.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.github.pagehelper.PageHelper;
import com.itechhero.app.module.common.utils.exceptions.ReqException;
import com.itechhero.app.module.common.utils.req.BodyParam;
import com.itechhero.app.module.common.utils.req.CMap;
import com.itechhero.app.module.common.utils.req.PageData;
import com.itechhero.app.module.common.utils.req.ResBean;
import com.itechhero.app.module.config.scheduler.SchedulerManager;
import com.itechhero.app.module.config.scheduler.TimerEntity;
import com.itechhero.app.module.edu.timer.mapper.TimerBeanMapper;
import com.itechhero.app.module.edu.timer.model.TimerBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@Transactional
public class TimerService {

    @Autowired
    private TimerBeanMapper timerBeanMapper;

    @Autowired
    private SchedulerManager schedulerManager;

    /*获取全部定时列表*/
    public List<TimerBean> getAll() {
        return this.timerBeanMapper.selectAll();
    }

    /*关闭定时*/
    public void closeTimer(String timerId) {
        TimerBean timerBean = this.timerBeanMapper.selectByPrimaryKey(timerId);
        timerBean.setIfStart(0);
        TimerEntity timerEntity = new TimerEntity();
        timerEntity.setTimerBean(timerBean);
        this.schedulerManager.startTimer(timerEntity);
        this.timerBeanMapper.updateByPrimaryKeySelective(timerBean);
    }

    /*开启定时*/
    public void startTimer(String timerId) {
        TimerBean timerBean = new TimerBean();
        timerBean.setId(timerId);
        timerBean.setIfStart(1);
        this.timerBeanMapper.updateByPrimaryKeySelective(timerBean);
    }

    /*测试定时任务*/
    @Transactional(readOnly = true)
    public void run() {
        //List<TimerBean> all = this.getAll();
        log.info("定时任务已经开启:\n");
    }

    /**
     * 分页获取定时列表
     * 作者: 吴 波
     * 时间: 2020-03-26 22:05
     * 笔名: 那年秋天的茄子^^
     */
    public PageData getTimers(CMap param) {
        PageHelper.startPage(param.getPageNum(), param.getPageSize());
        List<CMap> list = this.timerBeanMapper.getTimers(param);
        return PageData.builder("获取到分页定时", list);
    }

    /**
     * 修改定时任务
     * 作者: 吴 波
     * 时间: 2020-03-26 22:15
     * 笔名: 那年秋天的茄子^^
     */
    @Transactional
    public ResBean update(BodyParam bodyParam) {
        TimerBean updateTimerBean = new TimerBean();
        updateTimerBean.setId(bodyParam.getValue("id"));
        updateTimerBean.setTimerName(bodyParam.getValue("timerName"));
        updateTimerBean.setDescribe(bodyParam.getValue("describe"));
        updateTimerBean.setClassName(bodyParam.getValue("className"));
        updateTimerBean.setMethodName(bodyParam.getValue("methodName"));
        updateTimerBean.setCron(bodyParam.getValue("cron"));
        updateTimerBean.setIfStart(bodyParam.getInteger("ifStart"));

        this.timerBeanMapper.updateByPrimaryKeySelective(updateTimerBean);
        TimerBean reStartBean = this.timerBeanMapper.selectByPrimaryKey(updateTimerBean.getId());

        TimerEntity timerEntity = new TimerEntity();
        timerEntity.setTimerBean(reStartBean);
        ResBean resBean = schedulerManager.startTimer(timerEntity);
        if (resBean.isSuccess()) {
            return resBean;
        }
        throw ReqException.builder("定时修改失败");
    }

    /**
     * 添加定时任务
     * 作者: 吴 波
     * 时间: 2020-03-26 22:20
     * 笔名: 那年秋天的茄子^^
     */
    public ResBean save(BodyParam bodyParam) {
        TimerBean timerBean = bodyParam.getBeanByTypeReference(new TypeReference<TimerBean>() {
        });
        this.timerBeanMapper.insertSelective(timerBean);
        TimerEntity timerEntity = new TimerEntity();
        timerEntity.setTimerBean(timerBean);
        ResBean resBean = schedulerManager.startTimer(timerEntity);
        if (resBean.isSuccess()) {
            return resBean;
        }
        throw ReqException.builder("定时修改失败");
    }

    /**
     * 删除定时
     * 作者: 吴 波
     * 时间: 2020-03-27 1:35
     * 笔名: 那年秋天的茄子^^
     */
    public ResBean del(String id) {
        TimerBean timerBean = this.timerBeanMapper.selectByPrimaryKey(id);
        timerBean.setIfStart(0);
        TimerEntity timerEntity = new TimerEntity();
        timerEntity.setTimerBean(timerBean);
        this.schedulerManager.startTimer(timerEntity);

        this.timerBeanMapper.deleteByPrimaryKey(id);
        return ResBean.success();
    }
}

数据库SQL

drop table YJ_TIMER cascade constraints;

/*==============================================================*/
/* Table: YJ_TIMER                                              */
/*==============================================================*/
create table YJ_TIMER 
(
   ID                   VARCHAR(32)          not null,
   TIMER_NAME           VARCHAR(80),
   DESCRIBE             VARCHAR(300),
   CLASS_NAME           VARCHAR(50),
   METHOD_NAME          VARCHAR(60),
   CRON                 VARCHAR(100),
   IF_START             INT,
   constraint PK_YJ_TIMER primary key (ID)
);

comment on table YJ_TIMER is
'定时管理表';

comment on column YJ_TIMER.ID is
'唯一标识';

comment on column YJ_TIMER.TIMER_NAME is
'定时名称';

comment on column YJ_TIMER.DESCRIBE is
'描述';

comment on column YJ_TIMER.CLASS_NAME is
'服务类名';

comment on column YJ_TIMER.METHOD_NAME is
'函数名称';

comment on column YJ_TIMER.CRON is
'定时表达式';

comment on column YJ_TIMER.IF_START is
'是否启用';

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值