动态定时任务(动态定时器)

486 阅读9分钟

需求

在不重启服务的情况下,动态添加定时任务

效果

启动项目(有一个已存在的定时任务)

添加一个定时任务

查看控制台(动态添入定时任务)

实现步骤

pom.xml引入依赖

很多无关依赖可以自己删除,主要依赖:spring-boot-starter-quartz

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-job-quickstart</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatis-plus-job-quickstart</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
            <scope>runtime</scope>
        </dependency>

        <!-- Quartz定时任务 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

        <!-- Swagger API文档 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.9.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- json -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.8</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

yml添加线程池配置

task:
  pool:
    # 核心线程池大小
    core-pool-size: 10
    # 最大线程数
    max-pool-size: 30
    # 活跃时间
    keep-alive-seconds: 60
    # 队列容量
    queue-capacity: 50

数据库文件

/*
Navicat MySQL Data Transfer

Source Server         : 本地
Source Server Version : 80016
Source Host           : 127.0.0.1:3306
Source Database       : job

Target Server Type    : MYSQL
Target Server Version : 80016
File Encoding         : 65001

Date: 2020-10-21 17:52:45
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for quartz_job
-- ----------------------------
DROP TABLE IF EXISTS `quartz_job`;
CREATE TABLE `quartz_job` (
  `id` varchar(36) NOT NULL,
  `bean_name` varchar(32) DEFAULT NULL COMMENT 'Spring Bean名称',
  `cron_expression` varchar(32) DEFAULT NULL COMMENT 'cron 表达式',
  `is_pause` int(11) DEFAULT NULL COMMENT '状态:1暂停、0启用',
  `job_name` varchar(32) DEFAULT NULL COMMENT '任务名称',
  `method_name` varchar(32) DEFAULT NULL COMMENT '方法名称',
  `params` varchar(500) DEFAULT NULL COMMENT '参数',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `update_time` datetime DEFAULT NULL COMMENT '创建或更新日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of quartz_job
-- ----------------------------
INSERT INTO `quartz_job` VALUES ('1318039441581010946', 'visitsTask', '0/1 * * * * ?', '0', '更新访问量', 'run', '1318039441581010946', '测试数据', '2020-10-19 13:51:07');
INSERT INTO `quartz_job` VALUES ('1318852329113763842', 'visitsTask', '0/1 * * * * ?', '0', '新增定时', 'run', '000001', '测试', null);

-- ----------------------------
-- Table structure for quartz_log
-- ----------------------------
DROP TABLE IF EXISTS `quartz_log`;
CREATE TABLE `quartz_log` (
  `id` varchar(36) NOT NULL,
  `baen_name` varchar(200) DEFAULT NULL COMMENT 'Bean名称',
  `create_time` datetime DEFAULT NULL COMMENT '创建日期',
  `cron_expression` varchar(32) DEFAULT NULL COMMENT 'cron表达式',
  `exception_detail` longtext COMMENT '异常详细',
  `is_success` int(11) DEFAULT NULL COMMENT '状态 1:成功 0:失败',
  `job_name` varchar(200) DEFAULT NULL COMMENT '任务名称',
  `method_name` varchar(200) DEFAULT NULL COMMENT '方法名称',
  `params` varchar(200) DEFAULT NULL COMMENT '参数',
  `time` bigint(20) DEFAULT NULL COMMENT '耗时(毫秒)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of quartz_log
-- ----------------------------
INSERT INTO `quartz_log` VALUES ('1318832884848824321', 'visitsTask', null, '0/1 * * * * ?', '执行成功', '1', '更新访问量', 'run', '1315548171037986818,1318039441581010946', '1');
INSERT INTO `quartz_log` VALUES ('1318832888502063106', 'visitsTask', null, '0/1 * * * * ?', '执行成功', '1', '更新访问量', 'run', '1315548171037986818,1318039441581010946', '2');
INSERT INTO `quartz_log` VALUES ('1318852710766067715', 'visitsTask', null, '0/1 * * * * ?', '执行成功', '1', '新增定时', 'run', '000001', '1');
INSERT INTO `quartz_log` VALUES ('1318852714960371713', 'visitsTask', null, '0/1 * * * * ?', '执行成功', '1', '更新访问量', 'run', '1315548171037986818,1318039441581010946', '0');
INSERT INTO `quartz_log` VALUES ('1318852714960371714', 'visitsTask', null, '0/1 * * * * ?', '执行成功', '1', '新增定时', 'run', '000001', '0');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) COLLATE utf8mb4_croatian_ci DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `email` varchar(255) COLLATE utf8mb4_croatian_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_croatian_ci;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'Jone', '18', 'test1@baomidou.com');
INSERT INTO `user` VALUES ('2', 'Jack', '20', 'test2@baomidou.com');
INSERT INTO `user` VALUES ('3', 'Tom', '21', 'test3@baomidou.com');
INSERT INTO `user` VALUES ('4', 'Sandy', '22', 'test4@baomidou.com');

 

代码块

package com.mybatisplus.job.threadPool;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 配置属性类
 */
@Data
@Component
@ConfigurationProperties(prefix = "task.pool")
public class AsyncTaskProperties {

    private int corePoolSize;

    private int maxPoolSize;

    private int keepAliveSeconds;

    private int queueCapacity;
}
package com.mybatisplus.job.threadPool;

import org.springframework.stereotype.Component;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定义线程名称
 */
@Component
public class TheadFactoryName implements ThreadFactory {

    private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    public TheadFactoryName() {
        this("el-pool");
    }

    private TheadFactoryName(String name){
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();
        //此时namePrefix就是 name + 第几个用这个工厂创建线程池的
        this.namePrefix = name +
                POOL_NUMBER.getAndIncrement();
    }

    @Override
    public Thread newThread(Runnable r) {
        //此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程
        Thread t = new Thread(group, r,
                namePrefix + "-thread-"+threadNumber.getAndIncrement(),
                0);
        if (t.isDaemon()) {
            t.setDaemon(false);
        }
        if (t.getPriority() != Thread.NORM_PRIORITY) {
            t.setPriority(Thread.NORM_PRIORITY);
        }
        return t;
    }
}
package com.mybatisplus.job.threadPool;


import com.mybatisplus.job.SpringContextHolder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 用于获取自定义线程池
 */
public class ThreadPoolExecutorUtil {

    public static ThreadPoolExecutor getPoll(){
        AsyncTaskProperties properties = SpringContextHolder.getBean(AsyncTaskProperties.class);
        return new ThreadPoolExecutor(
                properties.getCorePoolSize(),
                properties.getMaxPoolSize(),
                properties.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(properties.getQueueCapacity()),
                new TheadFactoryName()
        );
    }
}
package com.mybatisplus.job;

import com.mybatisplus.entity.QuartzJob;
import com.mybatisplus.entity.QuartzLog;
import com.mybatisplus.job.threadPool.ThreadPoolExecutorUtil;
import com.mybatisplus.mapper.QuartzJobMapper;
import com.mybatisplus.mapper.QuartzLogMapper;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;

import javax.annotation.Resource;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

@Async
public class ExecutionJob extends QuartzJobBean {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String JOB_NAME = "TASK_";

    @Resource(name = "scheduler")
    private Scheduler scheduler;

    /** 该处仅供参考 */
    private final static ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll();


    @Override
    @SuppressWarnings("unchecked")
    protected void executeInternal(JobExecutionContext context) {
        QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
        // 获取spring bean
        QuartzJobMapper quartzJobMapper = SpringContextHolder.getBean(QuartzJobMapper.class);
        QuartzLogMapper quartzLogMapper = SpringContextHolder.getBean(QuartzLogMapper.class);

        QuartzLog log = new QuartzLog();
        log.setJobName(quartzJob.getJobName());

        log.setBaenName(quartzJob.getBeanName());
        log.setMethodName("run");
        log.setParams(quartzJob.getParams());
        long startTime = System.currentTimeMillis();
        log.setCronExpression(quartzJob.getCronExpression());
        try {
            // 执行任务
            logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName());
            QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),quartzJob.getParams());
            Future<?> future = EXECUTOR.submit(task);
            future.get();
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态
            log.setIsSuccess(1);
            log.setExceptionDetail("执行成功");
            logger.info("任务执行完毕,任务名称:{} 总共耗时:{} 毫秒", quartzJob.getJobName(), times);
        }  catch (Exception e) {
            logger.info("任务执行失败,失败原因{}",e.getMessage());
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态 1:成功 0:失败
            log.setIsSuccess(0);
            log.setExceptionDetail("定时执行失败");
            quartzJob.setIsPause(1);
            try {
                JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
                scheduler.pauseJob(jobKey);
            } catch (Exception ee){
                logger.info("定时任务暂停失败{}",ee.getMessage());
                throw new BadRequestException("定时任务暂停失败");
            }
            //更新状态
            quartzJobMapper.updateById(quartzJob);
        }  finally {
            quartzLogMapper.insert(log);
        }
    }
}
package com.mybatisplus.job;

import com.mybatisplus.entity.QuartzJob;
import com.mybatisplus.entity.QuartzLog;
import com.mybatisplus.job.threadPool.ThreadPoolExecutorUtil;
import com.mybatisplus.mapper.QuartzJobMapper;
import com.mybatisplus.mapper.QuartzLogMapper;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;

import javax.annotation.Resource;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

@Async
public class ExecutionJob extends QuartzJobBean {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String JOB_NAME = "TASK_";

    @Resource(name = "scheduler")
    private Scheduler scheduler;

    /** 该处仅供参考 */
    private final static ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll();


    @Override
    @SuppressWarnings("unchecked")
    protected void executeInternal(JobExecutionContext context) {
        QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
        // 获取spring bean
        QuartzJobMapper quartzJobMapper = SpringContextHolder.getBean(QuartzJobMapper.class);
        QuartzLogMapper quartzLogMapper = SpringContextHolder.getBean(QuartzLogMapper.class);

        QuartzLog log = new QuartzLog();
        log.setJobName(quartzJob.getJobName());

        log.setBaenName(quartzJob.getBeanName());
        log.setMethodName("run");
        log.setParams(quartzJob.getParams());
        long startTime = System.currentTimeMillis();
        log.setCronExpression(quartzJob.getCronExpression());
        try {
            // 执行任务
            logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName());
            QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),quartzJob.getParams());
            Future<?> future = EXECUTOR.submit(task);
            future.get();
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态
            log.setIsSuccess(1);
            log.setExceptionDetail("执行成功");
            logger.info("任务执行完毕,任务名称:{} 总共耗时:{} 毫秒", quartzJob.getJobName(), times);
        }  catch (Exception e) {
            logger.info("任务执行失败,失败原因{}",e.getMessage());
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态 1:成功 0:失败
            log.setIsSuccess(0);
            log.setExceptionDetail("定时执行失败");
            quartzJob.setIsPause(1);
            try {
                JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
                scheduler.pauseJob(jobKey);
            } catch (Exception ee){
                logger.info("定时任务暂停失败{}",ee.getMessage());
                throw new BadRequestException("定时任务暂停失败");
            }
            //更新状态
            quartzJobMapper.updateById(quartzJob);
        }  finally {
            quartzLogMapper.insert(log);
        }
    }
}
package com.mybatisplus.job;

import com.mybatisplus.entity.QuartzJob;
import com.mybatisplus.mapper.QuartzJobMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class JobRunner implements ApplicationRunner {

    @Autowired
    private QuartzJobMapper quartzJobMapper;

    @Autowired
    private QuartzManage quartzManage;

    /**
     * 项目启动时重新激活启用的定时任务
     * @param applicationArguments /
     */
    @Override
    public void run(ApplicationArguments applicationArguments){
        printSingleColor("",31,1,"--------------------注入定时任务---------------------");
        printSingleColor("",1,1,"");
        List<QuartzJob> quartzJobs = quartzJobMapper.getList();
        quartzJobs.stream().forEach(x ->{
            printSingleColor("",32,1,"注入--------------------->" + x.getJobName());
            printSingleColor("",1,1,"");
        });
        quartzJobs.forEach(quartzManage::addJob);
        printSingleColor("",34,1,"--------------------定时任务注入完成---------------------");
        printSingleColor("",1,1,"");
    }
    /**
     *
     * @param pattern 前面的图案 such as "=============="
     * @param code 颜色代号:背景颜色代号(41-46);前景色代号(31-36)
     * @param n 数字+m:1加粗;3斜体;4下划线
     * @param content 要打印的内容
     */
    public static void printSingleColor(String pattern,int code,int n,String content){
        System.out.format("%s\33[%d;%dm%s%n", pattern, code, n, content);
    }
}
package com.mybatisplus.job;

import org.quartz.Scheduler;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

/**
 * 定时任务配置
 * @author /
 */
@Configuration
public class QuartzConfig {

	/**
	 * 解决Job中注入Spring Bean为null的问题
	 */
	@Component("quartzJobFactory")
	public static class QuartzJobFactory extends AdaptableJobFactory {

		private final AutowireCapableBeanFactory capableBeanFactory;

		public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) {
			this.capableBeanFactory = capableBeanFactory;
		}

		@Override
		protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
			//调用父类的方法
			Object jobInstance = super.createJobInstance(bundle);
			capableBeanFactory.autowireBean(jobInstance);
			return jobInstance;
		}
	}

	/**
	 * 注入scheduler到spring
	 * @param quartzJobFactory /
	 * @return Scheduler
	 * @throws Exception /
	 */
	@Bean(name = "scheduler")
	public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception {
		SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
		factoryBean.setJobFactory(quartzJobFactory);
		factoryBean.afterPropertiesSet();
		Scheduler scheduler=factoryBean.getScheduler();
		scheduler.start();
		return scheduler;
	}
}
package com.mybatisplus.job;


import com.mybatisplus.entity.QuartzJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;

import static org.quartz.TriggerBuilder.newTrigger;

@Slf4j
@Component
public class QuartzManage {

    private static final String JOB_NAME = "TASK_";

    @Resource(name = "scheduler")
    private Scheduler scheduler;

    public void addJob(QuartzJob quartzJob){
        try {
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class).
                    withIdentity(JOB_NAME + quartzJob.getId()).build();

            //通过触发器名和cron 表达式创建 Trigger
            Trigger cronTrigger = newTrigger()
                    .withIdentity(JOB_NAME + quartzJob.getId())
                    .startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()))
                    .build();

            cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);

            //重置启动时间
            ((CronTriggerImpl)cronTrigger).setStartTime(new Date());

            //执行定时任务
            scheduler.scheduleJob(jobDetail,cronTrigger);

            // 暂停任务
            if (quartzJob.getIsPause().equals(1)) {
                pauseJob(quartzJob);
            }
        } catch (Exception e){
            log.error("创建定时任务失败", e);
            throw new BadRequestException("创建定时任务失败");
        }
    }

    /**
     * 更新job cron表达式
     * @param quartzJob /
     */
    public void updateJobCron(QuartzJob quartzJob){
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if(trigger == null){
                addJob(quartzJob);
                trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            }
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression());
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //重置启动时间
            ((CronTriggerImpl)trigger).setStartTime(new Date());
            trigger.getJobDataMap().put(QuartzJob.JOB_KEY,quartzJob);

            scheduler.rescheduleJob(triggerKey, trigger);
            // 暂停任务
            if (quartzJob.getIsPause().equals(1)) {
                pauseJob(quartzJob);
            }
        } catch (Exception e){
            log.error("更新定时任务失败", e);
            throw new BadRequestException("更新定时任务失败");
        }

    }

    /**
     * 删除一个job
     * @param quartzJob /
     */
    public void deleteJob(QuartzJob quartzJob){
        try {
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.pauseJob(jobKey);
            scheduler.deleteJob(jobKey);
        } catch (Exception e){
            log.error("删除定时任务失败", e);
            throw new BadRequestException("删除定时任务失败");
        }
    }

    /**
     * 恢复一个job
     * @param quartzJob /
     */
    public void resumeJob(QuartzJob quartzJob){
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if(trigger == null) {
                addJob(quartzJob);
            }
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.resumeJob(jobKey);
        } catch (Exception e){
            log.error("恢复定时任务失败", e);
            throw new BadRequestException("恢复定时任务失败");
        }
    }

    /**
     * 立即执行job
     * @param quartzJob /
     */
    public void runJobNow(QuartzJob quartzJob){
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if(trigger == null) {
                addJob(quartzJob);
            }
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(QuartzJob.JOB_KEY, quartzJob);
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.triggerJob(jobKey,dataMap);
        } catch (Exception e){
            log.error("定时任务执行失败", e);
            throw new BadRequestException("定时任务执行失败");
        }
    }

    /**
     * 暂停一个job
     * @param quartzJob /
     */
    public void pauseJob(QuartzJob quartzJob){
        try {
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.pauseJob(jobKey);
        } catch (Exception e){
            log.error("定时任务暂停失败", e);
            throw new BadRequestException("定时任务暂停失败");
        }
    }
}
package com.mybatisplus.job;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

@Slf4j
public class QuartzRunnable implements Callable {

	private Object target;
	private Method method;
	private String params;

	QuartzRunnable(String beanName, String methodName, String params)
			throws NoSuchMethodException, SecurityException {
		this.target = SpringContextHolder.getBean(beanName);
		this.params = params;

		if (StringUtils.isNotBlank(params)) {
			this.method = target.getClass().getDeclaredMethod(methodName, String.class);
		} else {
			this.method = target.getClass().getDeclaredMethod(methodName);
		}
	}

	@Override
	public Object call() throws Exception {
		ReflectionUtils.makeAccessible(method);
		if (StringUtils.isNotBlank(params)) {
			method.invoke(target, params);
		} else {
			method.invoke(target);
		}
		return null;
	}
}
package com.mybatisplus.job;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@Slf4j
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext = null;

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        assertContextInjected();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    public static <T> T getBean(Class<T> requiredType) {
        assertContextInjected();
        return applicationContext.getBean(requiredType);
    }

    /**
     * 检查ApplicationContext不为空.
     */
    private static void assertContextInjected() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
                    ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
        }
    }

    /**
     * 清除SpringContextHolder中的ApplicationContext为Null.
     */
    private static void clearHolder() {
        log.debug("清除SpringContextHolder中的ApplicationContext:"
                + applicationContext);
        applicationContext = null;
    }

    @Override
    public void destroy(){
        SpringContextHolder.clearHolder();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringContextHolder.applicationContext != null) {
            log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
        }
        SpringContextHolder.applicationContext = applicationContext;
    }
}
package com.mybatisplus.job.task;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class VisitsTask {

    public void run(String str){
        log.info("进入定时任务 -------------------------------> 【" + str + "】");
    }

}

Controller

package com.mybatisplus.controller;

import com.mybatisplus.entity.QuartzJob;
import com.mybatisplus.job.QuartzManage;
import com.mybatisplus.mapper.QuartzJobMapper;
import com.mybatisplus.utils.ResponseEntityT;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @description:动态定时器
 * @modified By:
 * @version: 1.0$
 */
@Api(tags="动态定时器")
@RestController
@RequestMapping("/job")
public class JobController {

    @Autowired
    private QuartzJobMapper quartzJobMapper;

    @Autowired
    private QuartzManage quartzManage;

    /**
     * @return
     */
    @ApiOperation(value="定时器列表-列表查询", notes="定时器列表")
    @GetMapping(value = "/list")
    public ResponseEntityT<List<QuartzJob>> queryPageList() {
        return ResponseEntityT.OK(quartzJobMapper.getList());
    }

    /**
     *   添加
     * @param quartzJob
     * @return
     */
    @ApiOperation(value="添加", notes="添加")
    @PostMapping(value = "/add")
    public ResponseEntityT<?> add(@RequestBody QuartzJob quartzJob,HttpServletRequest req) {
        quartzJob.setIsPause(0);
        quartzJobMapper.insert(quartzJob);
        quartzManage.addJob(quartzJob);
        return ResponseEntityT.OK("添加成功!");
    }
    /**
     * 暂停/启动/删除
     * @param quartzJob
     * @return
     */
    @ApiOperation(value="暂停/启动/删除", notes="isPause = 1 暂停 2.立即启动 3.删除 0.恢复")
    @PutMapping(value = "/edit")
    public ResponseEntityT<?> edit(@RequestBody QuartzJob quartzJob) {
        QuartzJob findQuartzJob = quartzJobMapper.selectById(quartzJob.getId());
        if(quartzJob==null) {
            return ResponseEntityT.error("未找到对应数据");
        }
        if(quartzJob.getIsPause() == 0){
            quartzManage.resumeJob(findQuartzJob);
            findQuartzJob.setIsPause(0);
            quartzJobMapper.updateById(findQuartzJob);
        }
        if(quartzJob.getIsPause() == 1){
            quartzManage.pauseJob(findQuartzJob);
            findQuartzJob.setIsPause(1);
            quartzJobMapper.updateById(findQuartzJob);
        }
        if(quartzJob.getIsPause() == 2){
            quartzManage.runJobNow(findQuartzJob);
        }
        if(quartzJob.getIsPause() == 3){
            quartzManage.deleteJob(findQuartzJob);
            quartzJobMapper.deleteById(findQuartzJob);
        }
        return ResponseEntityT.OK("操作成功!");
    }

}

 

源码地址:gitee.com/tangzhengfe…