SpringBoot+Quartz 动态实现定时任务

背景

最近做项目,需要定时的去执行某一项任务,针对间隔循环,每天,每周,每月等,于是翻阅了一些资料和一些项目,最后成功实现了动态的创建一个定时任务。这里选择的是spring集成Quartz

核心依赖

		<dependency>
            <groupId>org.springframerwork.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.2</version>
        </dependency>

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

核心 QuartzConfig 配置类

	/**
     * 
     */
    @Component("quartzJobFactory")
    public class QuartzJobFactory extends AdaptableJobFactory {

        @Autowired
        private AutowireCapableBeanFactory capableBeanFactory;

        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {

            //调用父类的方法
            Object jobInstance = super.createJobInstance(bundle);
            capableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    }

    /**
     * 注入scheduler到spring
     * @param quartzJobFactory
     * @return
     * @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;
    }

在这里注入了核心的 quartzJobFactory 和 scheduler,接下来就是创建定时任务的工具类

ExecutionJob 日志记录

public class ExecutionJob extends QuartzJobBean {

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

    private ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Override
    protected void executeInternal(JobExecutionContext context) {
        QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
        // 获取spring bean
        QuartzJobService quartzJobService = SpringContextHolder.getBean("quartzJobService");

        QuartzLog log = new QuartzLog();
        log.setJobName(quartzJob.getJobName());
        log.setBeanName(quartzJob.getBeanName());
        log.setMethodName(quartzJob.getMethodName());
        log.setParams(quartzJob.getParams());
        long startTime = System.currentTimeMillis();
        log.setCronExpression(quartzJob.getCronExpression());
        log.setType(quartzJob.getType());
        try {
            // 执行任务
            logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName());
            QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),
                    quartzJob.getParams());
            Future<?> future = executorService.submit(task);
            future.get();
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态
            log.setIsSuccess(true);
            logger.info("任务执行完毕,任务名称:{} 总共耗时:{} 毫秒", quartzJob.getJobName(), times);
        } catch (Exception e) {
            logger.error("任务执行失败,任务名称:{}" + quartzJob.getJobName(), e);
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态 0:成功 1:失败
            log.setIsSuccess(false);
            log.setExceptionDetail(ThrowableUtil.getStackTrace(e));
            quartzJob.setIsPause(false);
            //更新状态
            quartzJobService.updateIsPause(quartzJob);
        } finally {
            //持久化日志到数据库
        }
    }
}

可以通过此类来记录定时任务执行的一些日志

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()).withMisfireHandlingInstructionDoNothing())
                    .build();

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

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

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

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

    /**
     * 更新job cron表达式
     * @param quartzJob
     * @throws SchedulerException
     */
    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()) {
                pauseJob(quartzJob);
            }
        } catch (Exception e){
            log.error("更新定时任务失败", e);
            throw new BadRequestException("更新定时任务失败");
        }

    }

    /**
     * 删除一个job
     * @param quartzJob
     * @throws SchedulerException
     */
    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
     * @throws SchedulerException
     */
    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
     * @throws SchedulerException
     */
    public void runAJobNow(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
     * @throws SchedulerException
     */
    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("定时任务暂停失败");
        }
    }

QuartzRunnable类

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;
    }
}

QuartzJob实体类

public static final String JOB_KEY = "JOB_KEY";

    @NotNull(groups = {Update.class})
    protected Long id;

    /**
     * 定时器名称
     */
    private String jobName;

    /**
     * Bean名称
     */
    @NotBlank
    private String beanName;

    /**
     * 方法名称
     */
    @NotBlank
    private String methodName;

    /**
     * 参数
     */
    private String params;

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

    /**
     * 状态
     */
    private Boolean isPause = false;

    /**
     * 备注
     */
    private String remark;

    /**
     * 创建日期
     */
    private Timestamp updateTime;

    //类型
    private Integer type;

通过实例化一个 quartzJob 实体来通过 QuartzManage 工具类创建开启一个定时任务,可以将这个实体持久化到数据库做系统初始化任务,也可以满足动态的创建定时任务

最后就是系统初始化的时候创建一些定时任务了:

@Component
@Slf4j
public class JobRunner implements ApplicationRunner {

    @Autowired
    private QuartzManage quartzManage;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        QuartzTask quartzTask  =new QuartzTask();
        quartzTask.run();
    }

    class QuartzTask implements Runnable {

        @Override
        public void run() {
            try {
                log.info("--------------------注入定时任务---------------------");
                QuartzJob quartzJob = new QuartzJob();
                quartzJob.setBeanName("testTask");
                quartzJob.setCronExpression("0/5 * * * * ?");
                quartzJob.setIsPause(false);
                quartzJob.setJobName("aaa");
                quartzJob.setMethodName("excute");
                quartzJob.setParams("2");
                quartzJob.setType(2);
                quartzJob.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                quartzManage.addJob(quartzJob);
            } catch (Exception e) {
                log.error("QuartzTask error:{}", e);
            }
        }
    }
}
@Component("testTask")
public class TestTask {
    public void excute(String id){
        System.out.println("任务执行,执行参数 = " + id);
    }
}

可以通过beanName指定一个定时任务类,通过methodName来调用方法,最后要注意的cron表达式不要写错了!
至此,可以成功的满足动态的创建一个定时任务

总结:具体的数据库的增删改查这里就不罗列了,相信crud大家都会!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态定时任务调度可以使用Spring Boot、Vue和Quartz实现。下面是一个基本的实现步骤: 1. 在Spring Boot中配置Quartz 在pom.xml文件中添加Quartz依赖: ```xml <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> ``` 在application.yml文件中添加Quartz的配置: ```yaml spring: quartz: job-store-type: jdbc jdbc: initialize-schema: always comment-prefix: QRTZ_ scheduler-name: MyScheduler properties: org: quartz: jobStore: driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate useProperties: true threadPool: threadCount: 3 plugin: shutdownhook: class: org.quartz.plugins.management.ShutdownHookPlugin jobInitializer: class: org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin filePath: quartz/jobs.xml ``` 2. 创建Job类 创建一个继承自Quartz的Job类,并实现execute方法。这个方法中就是我们要执行的定时任务。 ```java public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { //TODO: 执行定时任务的代码 } } ``` 3. 创建Trigger类 创建一个Trigger类,用于定义定时任务的调度规则。例如每天的凌晨1点执行一次任务。 ```java public class MyTrigger { public static Trigger getTrigger() { return TriggerBuilder.newTrigger() .withIdentity("MyTrigger", "MyGroup") .withSchedule(CronScheduleBuilder.cronSchedule("0 0 1 * * ? *")) .build(); } } ``` 4. 创建JobDetail类 创建一个JobDetail类,用于定义要执行的任务。 ```java public class MyJobDetail { public static JobDetail getJobDetail() { return JobBuilder.newJob(MyJob.class) .withIdentity("MyJob", "MyGroup") .storeDurably() .build(); } } ``` 5. 添加任务到Quartz中 创建一个SchedulerFactoryBean,用于将任务添加到Quartz中。 ```java @Configuration public class QuartzConfig { @Autowired private ApplicationContext applicationContext; @Bean public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setOverwriteExistingJobs(true); schedulerFactoryBean.setSchedulerName("MyScheduler"); schedulerFactoryBean.setAutoStartup(true); schedulerFactoryBean.setApplicationContext(applicationContext); Properties quartzProperties = new Properties(); quartzProperties.load(new ClassPathResource("quartz.properties").getInputStream()); schedulerFactoryBean.setQuartzProperties(quartzProperties); List<Trigger> triggers = new ArrayList<>(); triggers.add(MyTrigger.getTrigger()); Map<JobDetail, List<Trigger>> jobDetails = new HashMap<>(); jobDetails.put(MyJobDetail.getJobDetail(), triggers); schedulerFactoryBean.setJobDetails(jobDetails); return schedulerFactoryBean; } } ``` 6. 创建Vue前端界面 在Vue中创建一个前端界面,用于动态添加、修改和删除定时任务。界面中需要包含定时任务的名称、调度规则和执行方法。 7. 实现动态添加、修改和删除定时任务 在Vue中,可以使用axios来发送HTTP请求,将任务信息发送到Spring Boot后端。在后端中,可以通过Quartz动态添加、修改和删除定时任务。 8. 测试 启动Spring Boot应用程序,并访问Vue前端界面。添加一个定时任务,并查看任务是否按照调度规则执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值