基于quartz实现自定义时间的定时调度

本文介绍了一种使用Quartz实现自定义时间的定时调度的方法,避免了xxlJob和Spring注解的局限性。通过下载提供的代码,可以实现运行时通过代码动态调整调度计划。代码中包括了JobService、QuartzConfig、QuartzManage等关键组件,以及ExecutionJob类用于反射调用任务。此外,还展示了如何在项目启动时恢复启用的定时任务,并提供了测试用例。
摘要由CSDN通过智能技术生成

基于quartz实现自定义时间的定时调度

关于定时调度,大部分是使用类似定时调度平台xxlJob或者spring的定时调度注解是实现的。其中xxlJob的控制基于web管理页面,需要手工配置。spring的调度注解@Scheduled需要配置值,无法运行时更改。所以他们都无法在运行时通过代码或者其他自定义的方式实现动态调度。

基于开源的一些代码,我整理了一个基于quartz实现自定义时间的定时调度的方法,下载代码链接

https://download.csdn.net/download/ZHONGZEWEI/21117033

pom依赖

    <dependencies>


        <!--  定时调度依赖      -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>


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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!--     工具类   -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.0</version>
        </dependency>

    </dependencies>

定时调度执行计划实体类OperationPlan

OperationPlan用于配置定时调度的执行时间,执行名称,执行id等属性,可以自定义其他字段

@Data
@Builder
public class OperationPlan {

    public static final String JOB_KEY = "JOB_KEY";


    /**
     * 标识id
     */
    private Long id;


    /**
     * 任务名
     */
    private  String name;


    /**
     * cron表达式
     */
    private String cronExpression;



}

实际执行调度任务的JobService

JobService用于传入OperationPlan,针对OperationPlan做一次调度任务,是实际调度任务的执行者

package com.example.timer.service;

import cn.hutool.json.JSONUtil;
import com.example.timer.domain.OperationPlan;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;


@Service
@Slf4j
public class JobService {

    public void executeOperationPlan(OperationPlan operationPlan) {
        log.info("执行计划{}", JSONUtil.toJsonStr(operationPlan));

    }

}

QuartzConfig注入相关bean到spring容器

/**
 * 定时任务配置
 * @author /
 * @date 2019-01-07
 */
@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;
	}
}

QuartzManage处理定时调度的增删改等操作

在这里插入图片描述

package com.example.timer.timer;


import com.example.timer.domain.OperationPlan;
import com.example.timer.exp.BizException;
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;

/**
 * @author Zheng Jie
 * @date 2019-01-07
 */
@Slf4j
@Component
public class QuartzManage {

    private static final String JOB_NAME = "TASK_";

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

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

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

            cronTrigger.getJobDataMap().put(OperationPlan.JOB_KEY, operationPlan);

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

            //执行定时任务
            scheduler.scheduleJob(jobDetail, cronTrigger);
        } catch (Exception e) {
            log.error("创建定时任务失败", e);
            throw BizException.wrap("创建定时任务失败");
        }
    }

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

            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            log.error("更新定时任务失败", e);
            throw BizException.wrap("更新定时任务失败");
        }

    }

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

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

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

    /**
     * 暂停一个job
     *
     * @param operationPlan /
     */
    public void pauseJob(OperationPlan operationPlan) {
        try {
            JobKey jobKey = JobKey.jobKey(JOB_NAME + operationPlan.getId());
            scheduler.pauseJob(jobKey);
        } catch (Exception e) {
            log.error("定时任务暂停失败", e);
            throw BizException.wrap("定时任务暂停失败");
        }
    }
}

ExecutionJob接受调度任务并且基于反射调用jobService

在这里插入图片描述

package com.example.timer.timer;


import cn.hutool.core.exceptions.ExceptionUtil;
import com.example.timer.config.ThreadPoolExecutorUtil;
import com.example.timer.domain.OperationPlan;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 参考人人开源,https://gitee.com/renrenio/renren-security
 *
 * @author /
 * @date 2019-01-07
 */
@Async
@Slf4j
public class ExecutionJob extends QuartzJobBean {

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

    @Override
    public void executeInternal(JobExecutionContext context) {
        OperationPlan operationPlan = (OperationPlan) context.getMergedJobDataMap().get(OperationPlan.JOB_KEY);
        long startTime = System.currentTimeMillis();
        try {
            // 执行任务
            log.info("任务开始执行,任务名称:" + operationPlan.getName());

            QuartzRunnable task = new QuartzRunnable("jobService", "executeOperationPlan",
                    operationPlan);
            Future<?> future = EXECUTOR.submit(task);
            future.get();
            long times = System.currentTimeMillis() - startTime;
            log.info("任务执行完毕,任务名称:" + operationPlan.getName() + ", 执行时间:" + times + "毫秒");
        } catch (Exception e) {

            log.error("任务执行失败,任务名称:{},失败原因:{}" + operationPlan.getName(), ExceptionUtil.stacktraceToString(e));

        }
    }


}

项目启动时重新激活启用的定时任务

一般自定义的定时任务都是保存在数据库里面的,为了历史的调度任务不丢失,需要在应用启动时,重新激活启用的定时任务。

@Component
@Slf4j
public class JobRunner implements ApplicationRunner {


    @Resource
    private QuartzManage quartzManage;

    /**
     * 项目启动时重新激活启用的定时任务
     *
     * @param applicationArguments /
     */
    @Override
    public void run(ApplicationArguments applicationArguments) {
        log.info("--------------------注入定时任务---------------------");
        OperationPlan operationPlan = OperationPlan.builder().id(2L).name("每5秒执行一次").cronExpression("0/5 * * * * ? *").build();
        quartzManage.addJob(operationPlan);
        log.info("--------------------定时任务注入完成---------------------");
    }
}

测试

测试1:测试历史的,随项目启动时重新激活启用的定时任务

在这里插入图片描述

测试2:新增每秒执行一次的定时任务

我们通过新增一个接口job,通过调用接口的方式去动态增加调度任务

@RestController
@RequestMapping("/job")
public class JobController {



    @Resource
    QuartzManage quartzManage;


    @PostMapping()
    public String add(@RequestBody OperationPlan operationPlan) {
        quartzManage.addJob(operationPlan);
        return "动态添加自定义时间定时任务成功";
    }
   
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值