springboot集成quartz框架,实现定时任务

springboot集成quartz框架,实现定时任务

后端创建一个类和方法,前端直接输入类.方法名,然后定义执行时间以及是否启用

quartz三个重要组成部分:

调度器:Scheduler实现任务的管理;

任务:JobDetail(通过反射)创建具体的任务实例;

触发器:Trigger 根据调度时间执行具体任务

在这里插入图片描述

1. 添加依赖

确保在你的pom.xml文件中包含Quartz依赖

<dependencies>
    <!-- Spring Boot Starter for Quartz -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
</dependencies>

2. 创建一个通用的任务执行器

创建一个通用的任务执行器,用于通过反射机制执行指定类的指定方法

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class DynamicJob implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        String className = context.getJobDetail().getJobDataMap().getString("className");
        String methodName = context.getJobDetail().getJobDataMap().getString("methodName");

        try {
            Class<?> clazz = Class.forName(className);
            Object instance = clazz.getDeclaredConstructor().newInstance();
            Method method = clazz.getMethod(methodName);
            method.invoke(instance);
        } catch (Exception e) {
            throw new JobExecutionException(e);
        }
    }
}

3. 创建任务调度服务

创建一个服务,用于添加、删除和管理动态任务,Trigger实现根据具体的实例名+调度时间(jobName+cronExpression),执行任务:

import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class SchedulerService {

    @Autowired
    private Scheduler scheduler;

    public void scheduleJob(String jobName, String className, String methodName, String cronExpression) throws SchedulerException {
        JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
                .withIdentity(jobName)
                .usingJobData("className", className)
                .usingJobData("methodName", methodName)
                .build();

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobName + "Trigger")
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                .build();

        scheduler.scheduleJob(jobDetail, trigger);
    }

    public void deleteJob(String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);
        scheduler.deleteJob(jobKey);
    }
}

4. 创建控制器

创建一个控制器,用于接收前端请求,并调用调度服务的方法:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/scheduler")
public class SchedulerController {

    @Autowired
    private SchedulerService schedulerService;

    @PostMapping("/schedule")
    public String scheduleJob(@RequestParam String jobName, 
                              @RequestParam String className, 
                              @RequestParam String methodName, 
                              @RequestParam String cronExpression) {
        try {
            schedulerService.scheduleJob(jobName, className, methodName, cronExpression);
            return "Job scheduled successfully";
        } catch (Exception e) {
            return "Failed to schedule job: " + e.getMessage();
        }
    }

    @DeleteMapping("/delete")
    public String deleteJob(@RequestParam String jobName) {
        try {
            schedulerService.deleteJob(jobName);
            return "Job deleted successfully";
        } catch (Exception e) {
            return "Failed to delete job: " + e.getMessage();
        }
    }
}

5. 前端(未测试)

前端使用vue,提供一个表单,用户输入jobNameclassNamemethodName以及cronExpression,并通过HTTP请求将这些数据发送到后端API

注:cronExpression 是Quartz Cron触发器使用的一种特殊格式的字符串,用于定义任务的调度时间。Quartz的Cron表达式由6个或7个字段组成,每个字段代表一个时间单位,具体如下:[秒] [分] [小时] [日] [月] [星期] [年 (可选)];要每天下午六点执行一次任务,Quartz Cron表达式为:0 0 18 * * ?

创建一个Vue组件,提供一个表单,让用户输入调度任务的信息:

<template>
  <div>
    <h2>Schedule a Job</h2>
    <form @submit.prevent="scheduleJob">
      <div>
        <label for="jobName">Job Name:</label>
        <input type="text" v-model="jobName" required />
      </div>
      <div>
        <label for="className">Class Name:</label>
        <input type="text" v-model="className" required />
      </div>
      <div>
        <label for="methodName">Method Name:</label>
        <input type="text" v-model="methodName" required />
      </div>
      <div>
        <label for="cronExpression">Cron Expression:</label>
        <input type="text" v-model="cronExpression" required />
      </div>
      <button type="submit">Schedule Job</button>
    </form>

    <h2>Delete a Job</h2>
    <form @submit.prevent="deleteJob">
      <div>
        <label for="jobName">Job Name:</label>
        <input type="text" v-model="jobNameToDelete" required />
      </div>
      <button type="submit">Delete Job</button>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      jobName: '',
      className: '',
      methodName: '',
      cronExpression: '',
      jobNameToDelete: ''
    };
  },
  methods: {
    async scheduleJob() {
      try {
        const response = await fetch('/scheduler/schedule', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: new URLSearchParams({
            jobName: this.jobName,
            className: this.className,
            methodName: this.methodName,
            cronExpression: this.cronExpression,
          })
        });
        const result = await response.text();
        alert(result);
      } catch (error) {
        alert('Failed to schedule job: ' + error.message);
      }
    },
    async deleteJob() {
      try {
        const response = await fetch('/scheduler/delete', {
          method: 'DELETE',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: new URLSearchParams({
            jobName: this.jobNameToDelete,
          })
        });
        const result = await response.text();
        alert(result);
      } catch (error) {
        alert('Failed to delete job: ' + error.message);
      }
    }
  }
};
</script>

<style scoped>
form {
  margin-bottom: 20px;
}
div {
  margin-bottom: 10px;
}
label {
  margin-right: 10px;
}
button {
  margin-top: 10px;
}
</style>

6. 示例任务类

自定义任务类,示例:

package com.****.****.****.utils;

import org.springframework.stereotype.Component;

@Component
public class TestSchedulerMethod {
    public void testMethodScheduler(){
        System.out.println("测试定时方法"+System.currentTimeMillis());
    }
}

7.使用postMan测试

类名要用全路径名;cronExpression表达式:0 * * * * ? 每分钟执行一次
在这里插入图片描述

注意事项

  • 确保所有任务类都在Spring容器中管理(例如,使用@Component注解,即,6. 示例任务类,类名com...****.utils.TestSchedulerMethod,方法名:testMethodScheduler)。
  • 确保Quartz的Scheduler在Spring Boot启动时自动启动,可以在application.properties文件中配置:
spring.quartz.job-store-type=memory
spring.quartz.properties.org.quartz.scheduler.instanceName=MyScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO

8.扩展—持久化

上面示例,仅能将方法存入内存中,项目重启以后就失效了;
持久化提示:
创建一个表,调用scheduler的调度方法scheduler.scheduleJob(jobDetail,trigger);后,将用到的 jobName,className,methodName,cronExpression 信息存入表中,并创建一个init()方法,添加上@PostConstruct注解:用于在依赖注入完成后执行,将需要的定时方法重新加载

@Service
public class SchedulerService {

    @Autowired
    private Scheduler scheduler;
    
    public void scheduleJob(String jobName, String className, String methodName, String cronExpression) throws SchedulerException {
        JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
                .withIdentity(jobName)
                .usingJobData("className", className)
                .usingJobData("methodName", methodName)
                .build();
    
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobName + "Trigger")
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                .build();
    
        scheduler.scheduleJob(jobDetail, trigger);
        
         // 将定时任务保存到数据库里
        ScheduledTask task = new ScheduledTask();
        task.setJobName(jobName);
        task.setClassName(className);
        task.setMethodName(methodName);
        task.setCronExpression(cronExpression);
        scheduledTaskRepository.save(task);
    }
    
    public void deleteJob(String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);
        scheduler.deleteJob(jobKey);
        
        // 删除数据库里的定时任务
        ScheduledTask task = scheduledTaskRepository.findByJobName(jobName);
        if (task != null) {
            scheduledTaskRepository.delete(task);
        }
    }

     @PostConstruct
    public void init() throws SchedulerException {
        // 加载数据库里所有的定时任务
        List<ScheduledTask> tasks = scheduledTaskRepository.findAll();
        for (ScheduledTask task : tasks) {
            scheduleJob(task.getJobName(), task.getClassName(), task.getMethodName(), task.getCronExpression());
        }
    }
}

9.扩展—暂停和恢复(配合前端实现手动启停)

@RestController
@RequestMapping("/scheduler")
public class SchedulerController {

    @Autowired
    private SchedulerService schedulerService;

    // 其他已有方法...

    @PutMapping
    public String toggleJob(@RequestParam String jobName, @RequestParam boolean status) {
        try {
            if (status) {
                schedulerService.resumeJob(jobName);
            } else {
                schedulerService.pauseJob(jobName);
            }
            return "operation successfully";
        } catch (Exception e) {
            return "Failed to operation: " + e.getMessage();
        }
    }
}
@Service
public class SchedulerService {

    @Autowired
    private Scheduler scheduler;

    // 其他已有方法...
    // 若是实现了持久化pauseJob(String jobName)、resumeJob(String jobName)还需要配合持久化使用,表中增加字段持久化定时任务的执行状态
    public void pauseJob(String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);
        scheduler.pauseJob(jobKey);
    }

    public void resumeJob(String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);
        scheduler.resumeJob(jobKey);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值