SpringBoot实现定时任务,使用自带的定时任务以及调度框架quartz的配置使用

news2025/1/20 8:48:33

SpringBoot实现定时任务,使用自带的定时任务以及调度框架quartz的配置使用

文章目录

  • SpringBoot实现定时任务,使用自带的定时任务以及调度框架quartz的配置使用
    • 一. 使用SpringBoot自带的定时任务(适用于小型应用)
    • 二. 使用调度框架quartz(适用于中大型应用)
    • 三、数据库模式quartz的使用
        • 1. 创建数据库表
        • 2. 制定一个定时任务
        • 3. 写配置文件
        • 4. 创建接收和相应实体类
        • 5. 增删改查的controller层接口
        • 6. 编写接口测试代码

一. 使用SpringBoot自带的定时任务(适用于小型应用)

创建SpringBoot项目,创建SpringBootTestJob 类,作为定时任务类

 @Component
 @EnableScheduling
 public class SpringBootTestJob {

     @Scheduled(cron = "0/5 * * * * ?") // 每五秒执行一次
     private void test() {
         System.out.println("SpringBootTestJob TEST");
     }
 }
  • 适用性: 适合单体应用,不适合集群,没法实时更改定时任务状态和策略

启动后,可以看到,每五秒打印内容:
在这里插入图片描述

二. 使用调度框架quartz(适用于中大型应用)

  1. 增加依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
  1. 创建定时任务类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * @author SaoE
 * @date 2025/1/19 20:06
 */
public class TestJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("TestJob TEST");
    }
}
  1. 增加quartz配置类QuartzConfig
 @Configuration
 public class QuartzConfig {

     /**
      * 声明一个任务
      * @return
      */
     @Bean
     public JobDetail jobDetail() {
         return JobBuilder.newJob(TestJob.class)
                 .withIdentity("TestJob", "test")
                 .storeDurably()
                 .build();
     }

     /**
      * 声明一个触发器,什么时候触发这个任务
      * @return
      */
     @Bean
     public Trigger trigger() {
         return TriggerBuilder.newTrigger()
                 .forJob(jobDetail())
                 .withIdentity("trigger", "trigger")
                 .startNow()
                 .withSchedule(CronScheduleBuilder.cronSchedule("*/2 * * * * ?"))
                 .build();
     }
 }
  1. 启动主类
    可以看到控制台每两秒打印信息:
    在这里插入图片描述
  2. 禁止任务并发执行
    任务类增加注解@DisallowConcurrentExecution,修改如下:
@DisallowConcurrentExecution
public class TestJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("TestJob TEST开始");
         try {
             Thread.sleep(3000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
        System.out.println("TestJob TEST结束");
    }
}

此时打印输出为上一个任务结束后,下一个任务立即开始。如果没有这个注解,则会并发执行。

三、数据库模式quartz的使用

1. 创建数据库表
-- #
-- # Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
-- #
-- # PLEASE consider using mysql with innodb tables to avoid locking issues
-- #
-- # In your Quartz properties file, you'll need to set
-- # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-- #
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  JOB_NAME  VARCHAR(200) NOT NULL  comment 'job名称',
  JOB_GROUP VARCHAR(200) NOT NULL  comment 'job组',
  DESCRIPTION VARCHAR(250) NULL  comment '描述',
  JOB_CLASS_NAME   VARCHAR(250) NOT NULL  comment 'job类名',
  IS_DURABLE VARCHAR(1) NOT NULL  comment '是否持久化',
  IS_NONCONCURRENT VARCHAR(1) NOT NULL  comment '是否非同步',
  IS_UPDATE_DATA VARCHAR(1) NOT NULL  comment '是否更新数据',
  REQUESTS_RECOVERY VARCHAR(1) NOT NULL  comment '请求是否覆盖',
  JOB_DATA BLOB NULL  comment 'job数据',
  PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE QRTZ_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  TRIGGER_NAME VARCHAR(200) NOT NULL comment '触发器名称',
  TRIGGER_GROUP VARCHAR(200) NOT NULL  comment '触发器组',
  JOB_NAME  VARCHAR(200) NOT NULL  comment 'job名称',
  JOB_GROUP VARCHAR(200) NOT NULL  comment 'job组',
  DESCRIPTION VARCHAR(250) NULL  comment '描述',
  NEXT_FIRE_TIME BIGINT(13) NULL  comment '下一次触发时间',
  PREV_FIRE_TIME BIGINT(13) NULL  comment '前一次触发时间',
  PRIORITY INTEGER NULL  comment '等级',
  TRIGGER_STATE VARCHAR(16) NOT NULL  comment '触发状态',
  TRIGGER_TYPE VARCHAR(8) NOT NULL  comment '触发类型',
  START_TIME BIGINT(13) NOT NULL  comment '开始时间',
  END_TIME BIGINT(13) NULL  comment '结束时间',
  CALENDAR_NAME VARCHAR(200) NULL  comment '日程名称',
  MISFIRE_INSTR SMALLINT(2) NULL  comment '未触发实例',
  JOB_DATA BLOB NULL  comment 'job数据',
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
  REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  TRIGGER_NAME VARCHAR(200) NOT NULL  comment '触发器名称',
  TRIGGER_GROUP VARCHAR(200) NOT NULL  comment '触发器组',
  REPEAT_COUNT BIGINT(7) NOT NULL  comment '重复执行次数',
  REPEAT_INTERVAL BIGINT(12) NOT NULL  comment '重复执行间隔',
  TIMES_TRIGGERED BIGINT(10) NOT NULL  comment '已经触发次数',
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CRON_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  TRIGGER_NAME VARCHAR(200) NOT NULL  comment '触发器名称',
  TRIGGER_GROUP VARCHAR(200) NOT NULL  comment '触发器组',
  CRON_EXPRESSION VARCHAR(200) NOT NULL  comment 'cron表达式',
  TIME_ZONE_ID VARCHAR(80)  comment '时区',
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  TRIGGER_NAME VARCHAR(200) NOT NULL  comment '触发器名称',
  TRIGGER_GROUP VARCHAR(200) NOT NULL  comment '触发器组',
  STR_PROP_1 VARCHAR(512) NULL  comment '开始配置1',
  STR_PROP_2 VARCHAR(512) NULL  comment '开始配置2',
  STR_PROP_3 VARCHAR(512) NULL  comment '开始配置3',
  INT_PROP_1 INT NULL  comment 'int配置1',
  INT_PROP_2 INT NULL  comment 'int配置2',
  LONG_PROP_1 BIGINT NULL  comment 'long配置1',
  LONG_PROP_2 BIGINT NULL  comment 'long配置2',
  DEC_PROP_1 NUMERIC(13,4) NULL  comment '配置描述1',
  DEC_PROP_2 NUMERIC(13,4) NULL  comment '配置描述2',
  BOOL_PROP_1 VARCHAR(1) NULL  comment 'bool配置1',
  BOOL_PROP_2 VARCHAR(1) NULL  comment 'bool配置2',
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_BLOB_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  TRIGGER_NAME VARCHAR(200) NOT NULL  comment '触发器名称',
  TRIGGER_GROUP VARCHAR(200) NOT NULL  comment '触发器组',
  BLOB_DATA BLOB NULL  comment '数据',
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CALENDARS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  CALENDAR_NAME  VARCHAR(200) NOT NULL comment '日程名称',
  CALENDAR BLOB NOT NULL  comment '日程数据',
  PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  TRIGGER_GROUP  VARCHAR(200) NOT NULL  comment '触发器组',
  PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_FIRED_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  ENTRY_ID VARCHAR(95) NOT NULL  comment 'entryId',
  TRIGGER_NAME VARCHAR(200) NOT NULL  comment '触发器名称',
  TRIGGER_GROUP VARCHAR(200) NOT NULL  comment '触发器组',
  INSTANCE_NAME VARCHAR(200) NOT NULL  comment '实例名称',
  FIRED_TIME BIGINT(13) NOT NULL  comment '执行时间',
  SCHED_TIME BIGINT(13) NOT NULL  comment '定时任务时间',
  PRIORITY INTEGER NOT NULL  comment '等级',
  STATE VARCHAR(16) NOT NULL  comment '状态',
  JOB_NAME VARCHAR(200) NULL  comment 'job名称',
  JOB_GROUP VARCHAR(200) NULL  comment 'job组',
  IS_NONCONCURRENT VARCHAR(1) NULL  comment '是否异步',
  REQUESTS_RECOVERY VARCHAR(1) NULL  comment '是否请求覆盖',
  PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);
CREATE TABLE QRTZ_SCHEDULER_STATE
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  INSTANCE_NAME VARCHAR(200) NOT NULL  comment '实例名称',
  LAST_CHECKIN_TIME BIGINT(13) NOT NULL  comment '最近检入时间',
  CHECKIN_INTERVAL BIGINT(13) NOT NULL  comment '检入间隔',
  PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);
CREATE TABLE QRTZ_LOCKS
(
  SCHED_NAME VARCHAR(120) NOT NULL  comment '定时任务名称',
  LOCK_NAME  VARCHAR(40) NOT NULL  comment 'lock名称',
  PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);

2. 制定一个定时任务

和上面一样的TestJob

@DisallowConcurrentExecution
public class TestJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("TestJob TEST开始");
         try {
             Thread.sleep(3000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
        System.out.println("TestJob TEST结束");
    }
}
3. 写配置文件

MyJobFactory.java:

@Component
public class MyJobFactory extends SpringBeanJobFactory {

    @Resource
    private AutowireCapableBeanFactory beanFactory;

    /**
     * 这里覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire。
     */
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);
        beanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

SchedulerConfig.java:

@Configuration
public class SchedulerConfig {

    @Resource
    private MyJobFactory myJobFactory;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("dataSource") DataSource dataSource) throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);
        factory.setJobFactory(myJobFactory);
        factory.setStartupDelay(2);
        return factory;
    }
}
4. 创建接收和相应实体类

接收实体类CronJobReq.java:

public class CronJobReq {
    private String group;

    private String name;

    private String description;

    private String cronExpression;

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("CronJobDto{");
        sb.append("cronExpression='").append(cronExpression).append('\'');
        sb.append(", group='").append(group).append('\'');
        sb.append(", name='").append(name).append('\'');
        sb.append(", description='").append(description).append('\'');
        sb.append('}');
        return sb.toString();
    }

    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

响应实体类CronJobResp.java:

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class CronJobResp {
    private String group;

    private String name;

    private String description;

    private String state;

    private String cronExpression;

    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date nextFireTime;

    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date preFireTime;

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("CronJobDto{");
        sb.append("cronExpression='").append(cronExpression).append('\'');
        sb.append(", group='").append(group).append('\'');
        sb.append(", name='").append(name).append('\'');
        sb.append(", description='").append(description).append('\'');
        sb.append(", state='").append(state).append('\'');
        sb.append(", nextFireTime=").append(nextFireTime);
        sb.append(", preFireTime=").append(preFireTime);
        sb.append('}');
        return sb.toString();
    }

    public String getGroup() {
        return group;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getNextFireTime() {
        return nextFireTime;
    }

    public void setNextFireTime(Date nextFireTime) {
        this.nextFireTime = nextFireTime;
    }

    public Date getPreFireTime() {
        return preFireTime;
    }

    public void setPreFireTime(Date preFireTime) {
        this.preFireTime = preFireTime;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}
5. 增删改查的controller层接口

任务和quartz通过接口来关联。CronJobReq类的name属性会告诉quartz操作哪个定时任务。

JobController.java:

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

    private static Logger LOG = LoggerFactory.getLogger(JobController.class);

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    @RequestMapping(value = "/run")
    public CommonResp<Object> run(@RequestBody CronJobReq cronJobReq) throws SchedulerException {
        String jobClassName = cronJobReq.getName();
        String jobGroupName = cronJobReq.getGroup();
        LOG.info("手动执行任务开始:{}, {}", jobClassName, jobGroupName);
        schedulerFactoryBean.getScheduler().triggerJob(JobKey.jobKey(jobClassName, jobGroupName));
        return new CommonResp<>();
    }

    @RequestMapping(value = "/add")
    public CommonResp add(@RequestBody CronJobReq cronJobReq) {
        String jobClassName = cronJobReq.getName();
        String jobGroupName = cronJobReq.getGroup();
        String cronExpression = cronJobReq.getCronExpression();
        String description = cronJobReq.getDescription();
        LOG.info("创建定时任务开始:{},{},{},{}", jobClassName, jobGroupName, cronExpression, description);
        CommonResp commonResp = new CommonResp();

        try {
            // 通过SchedulerFactory获取一个调度器实例
            Scheduler sched = schedulerFactoryBean.getScheduler();

            // 启动调度器
            sched.start();

            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobClassName)).withIdentity(jobClassName, jobGroupName).build();

            //表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName).withDescription(description).withSchedule(scheduleBuilder).build();

            sched.scheduleJob(jobDetail, trigger);

        } catch (SchedulerException e) {
            LOG.error("创建定时任务失败:" + e);
            commonResp.setSuccess(false);
            commonResp.setMessage("创建定时任务失败:调度异常");
        } catch (ClassNotFoundException e) {
            LOG.error("创建定时任务失败:" + e);
            commonResp.setSuccess(false);
            commonResp.setMessage("创建定时任务失败:任务类不存在");
        }
        LOG.info("创建定时任务结束:{}", commonResp);
        return commonResp;
    }

    @RequestMapping(value = "/pause")
    public CommonResp pause(@RequestBody CronJobReq cronJobReq) {
        String jobClassName = cronJobReq.getName();
        String jobGroupName = cronJobReq.getGroup();
        LOG.info("暂停定时任务开始:{},{}", jobClassName, jobGroupName);
        CommonResp commonResp = new CommonResp();
        try {
            Scheduler sched = schedulerFactoryBean.getScheduler();
            sched.pauseJob(JobKey.jobKey(jobClassName, jobGroupName));
        } catch (SchedulerException e) {
            LOG.error("暂停定时任务失败:" + e);
            commonResp.setSuccess(false);
            commonResp.setMessage("暂停定时任务失败:调度异常");
        }
        LOG.info("暂停定时任务结束:{}", commonResp);
        return commonResp;
    }

    @RequestMapping(value = "/resume")
    public CommonResp resume(@RequestBody CronJobReq cronJobReq) {
        String jobClassName = cronJobReq.getName();
        String jobGroupName = cronJobReq.getGroup();
        LOG.info("重启定时任务开始:{},{}", jobClassName, jobGroupName);
        CommonResp commonResp = new CommonResp();
        try {
            Scheduler sched = schedulerFactoryBean.getScheduler();
            sched.resumeJob(JobKey.jobKey(jobClassName, jobGroupName));
        } catch (SchedulerException e) {
            LOG.error("重启定时任务失败:" + e);
            commonResp.setSuccess(false);
            commonResp.setMessage("重启定时任务失败:调度异常");
        }
        LOG.info("重启定时任务结束:{}", commonResp);
        return commonResp;
    }

    @RequestMapping(value = "/reschedule")
    public CommonResp reschedule(@RequestBody CronJobReq cronJobReq) {
        String jobClassName = cronJobReq.getName();
        String jobGroupName = cronJobReq.getGroup();
        String cronExpression = cronJobReq.getCronExpression();
        String description = cronJobReq.getDescription();
        LOG.info("更新定时任务开始:{},{},{},{}", jobClassName, jobGroupName, cronExpression, description);
        CommonResp commonResp = new CommonResp();
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTriggerImpl trigger1 = (CronTriggerImpl) scheduler.getTrigger(triggerKey);
            trigger1.setStartTime(new Date()); // 重新设置开始时间
            CronTrigger trigger = trigger1;

            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withDescription(description).withSchedule(scheduleBuilder).build();

            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            LOG.error("更新定时任务失败:" + e);
            commonResp.setSuccess(false);
            commonResp.setMessage("更新定时任务失败:调度异常");
        }
        LOG.info("更新定时任务结束:{}", commonResp);
        return commonResp;
    }

    @RequestMapping(value = "/delete")
    public CommonResp delete(@RequestBody CronJobReq cronJobReq) {
        String jobClassName = cronJobReq.getName();
        String jobGroupName = cronJobReq.getGroup();
        LOG.info("删除定时任务开始:{},{}", jobClassName, jobGroupName);
        CommonResp commonResp = new CommonResp();
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));
            scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));
            scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));
        } catch (SchedulerException e) {
            LOG.error("删除定时任务失败:" + e);
            commonResp.setSuccess(false);
            commonResp.setMessage("删除定时任务失败:调度异常");
        }
        LOG.info("删除定时任务结束:{}", commonResp);
        return commonResp;
    }

    @RequestMapping(value="/query")
    public CommonResp query() {
        LOG.info("查看所有定时任务开始");
        CommonResp commonResp = new CommonResp();
        List<CronJobResp> cronJobDtoList = new ArrayList();
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            for (String groupName : scheduler.getJobGroupNames()) {
                for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
                    CronJobResp cronJobResp = new CronJobResp();
                    cronJobResp.setName(jobKey.getName());
                    cronJobResp.setGroup(jobKey.getGroup());

                    //get job's trigger
                    List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);
                    CronTrigger cronTrigger = (CronTrigger) triggers.get(0);
                    cronJobResp.setNextFireTime(cronTrigger.getNextFireTime());
                    cronJobResp.setPreFireTime(cronTrigger.getPreviousFireTime());
                    cronJobResp.setCronExpression(cronTrigger.getCronExpression());
                    cronJobResp.setDescription(cronTrigger.getDescription());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(cronTrigger.getKey());
                    cronJobResp.setState(triggerState.name());

                    cronJobDtoList.add(cronJobResp);
                }

            }
        } catch (SchedulerException e) {
            LOG.error("查看定时任务失败:" + e);
            commonResp.setSuccess(false);
            commonResp.setMessage("查看定时任务失败:调度异常");
        }
        commonResp.setContent(cronJobDtoList);
        LOG.info("查看定时任务结束:{}", commonResp);
        return commonResp;
    }

}
6. 编写接口测试代码

test.http:

POST http://localhost:8000/batch/admin/job/add
Content-Type: application/json

{
  "name": "com.mystudy.train.batch.job.TestJob",
  "jobGroupName": "default",
  "cronExpression": "*/2 * * * * ?",
  "desc": "test job"
}

###

GET http://localhost:8000/batch/admin/job/query

###

POST http://localhost:8000/batch/admin/job/pause
Content-Type: application/json

{
  "name": "com.mystudy.train.batch.job.TestJob",
  "jobGroupName": "default"
}

###

POST http://localhost:8000/batch/admin/job/resume
Content-Type: application/json

{
  "name": "com.mystudy.train.batch.job.TestJob",
  "jobGroupName": "default"
}

###

POST http://localhost:8000/batch/admin/job/reschedule
Content-Type: application/json

{
"name": "com.mystudy.train.batch.job.TestJob",
"jobGroupName": "default",
"cronExpression": "*/5 * * * * ?",
"desc": "test job"
}

###

POST http://localhost:8000/batch/admin/job/delete
Content-Type: application/json

{
"name": "com.mystudy.train.batch.job.TestJob",
"jobGroupName": "default"
}

###

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2279292.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JsonViewer】Json格式化

使用 Notepad 对 Json 数据进行格式化处理&#xff0c;使数据在结构上更清晰 1.在线安装 安装之后&#xff0c;重启应用&#xff0c;在插件菜单栏即可看到 JsonViewer 选项&#xff0c;在 Notepad 中放入 Json 数据&#xff0c;点击 Format Json 进行数据格式化 2.离线安装 …

基于开源云原生数据仓库 ByConity 体验多种数据分析场景

基于开源云原生数据仓库 ByConity 体验多种数据分析场景 业务背景什么是 ByConity上手实测 环境要求测试操作 远程登录 ECS 服务器windows10 自带连接工具 执行查询 ByConity 相对于 ELT 能力的优化 提升并行度任务级重试并行写入简化数据链路 业务背景 大家都知道&#xff0c;…

图论DFS:黑红树

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 DFS 算法&#xff1a;记忆化搜索DFS 算法&#xf…

【2025最新】国内中文版 ChatGPT镜像网站整理合集,GPT最新模型4o1,4o,4o-mini分类区别,镜像站是什么

0.立即chat liji.chat 1.两者对比 官网立即Chat访问难度需要魔法直接访问支付手段国际支付国内支付封禁策略检测节点&#xff0c;随时封禁不会封禁价格每月140元订阅费用每年70元虚拟卡一天仅需4.88&#xff0c;一月低至48.88随用性需要用时必须开月度订阅可按天周月&#xff…

AI News(1/19/2025):Microsoft AutoGen v0.4:走向更智能 AI 代理的转折点

1、Microsoft AutoGen v0.4&#xff1a;走向更智能 AI 代理的转折点[1] 微软发布的AutoGen v0.4是AI代理领域的一个重要进步。该框架采用异步事件驱动架构&#xff0c;使代理能够并行执行任务&#xff0c;提高任务执行速度和资源利用效率&#xff0c;这对于多代理系统尤为重要…

4. LwIP_网络数据包管理

概述 协议栈的本质&#xff1a; TCP/IP协议栈的实现&#xff0c;本质上就是对数据包的管理。在LwIP中&#xff0c;定义了一个pbuf结构体对数据包进行管理。 pbuf管理数据包的步骤&#xff1a; 1、用户产生要传输的数据 2、用户在内存堆/内存池中申请一个pbuf结构体 3、将…

NPC与AI深度融合结合雷鸟X3Pro AR智能眼镜:引领游戏行业沉浸式与增强现实新纪元的畅想

if… NPC&#xff08;非玩家角色&#xff09;与AI&#xff08;人工智能&#xff09;的深度融合&#xff0c;正引领游戏行业迈向一个全新的沉浸式与增强现实&#xff08;AR&#xff09;相结合的新时代。这一创新不仅预示着游戏体验的质变&#xff0c;更可能全面革新游戏设计与叙…

Redis系列之底层数据结构字典Dict

Redis系列之底层数据结构字典Dict Dict数据结构 Dict是Redis数据结构中使用最为频繁的复合型数据结构&#xff0c;本质上是一个哈希表 查看redis6.0版本的源码&#xff0c;链接&#xff1a;https://github.com/redis/redis/blob/6.0/src/dict.h 哈希表的结构定义&#xff1…

前端【3】--CSS布局,CSS实现横向布局,盒子模型

盒子分类 1、块级盒子 2、内联级盒子 3、内联块级盒子 4、弹性盒子 5、盒子内部分区 方法一&#xff1a;使用 float 普通盒子实现横向布局 方法二&#xff1a;使用 display: inline-block 内联块级元素实现横向布局 方法三&#xff1a;使用弹性盒子 flexbox&#xff0…

Go Map 源码分析(一)

Go语言中的map是通过哈希表实现的&#xff0c;其底层结构和实现机制如下&#xff1a; 一、hash 结构 hmap结构体&#xff1a;是map的头部结构&#xff0c;主要字段及含义如下&#xff1a; count&#xff1a;表示当前哈希表中的元素数量&#xff0c;与len()函数相对应。flags…

【大数据2025】Yarn 总结

分布式资源管理系统讲解总结 一、引言 围绕分布式资源管理系统展开&#xff0c;重点涵盖 Yarn 的简介、原理、资源调度策略以及运维和管理&#xff0c;旨在让学员全面掌握相关知识。Yet Another Resource Negotiator 二、Yarn 诞生背景 在 Hadoop 1.X 中仅有 HDFS 和 MapRe…

【AI日记】25.01.19

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI kaggle 比赛&#xff1a;Forecasting Sticker Sales 读书 书名&#xff1a;自由宪章阅读原因&#xff1a;作者哈耶克&#xff0c;诺贝尔经济学奖得主&#xff0c;之前读过他的 《通往奴役…

5.最长回文子串--力扣

给你一个字符串 s&#xff0c;找到 s 中最长的 回文子串。 示例 1&#xff1a; 输入&#xff1a;s “babad” 输出&#xff1a;“bab” 解释&#xff1a;“aba” 同样是符合题意的答案。 示例 2&#xff1a; 输入&#xff1a;s “cbbd” 输出&#xff1a;“bb” 原题如上&…

GCPAAS/DashBoard:完全免费的仪表盘设计,基于Vue+ElementUI+G2Plot+Echarts,开源代码,简单易用!还在等什么呢

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 GCPAAS/DashBoard&#xff0c;一款基于SpringBoot、MyBatisPlus、ElementUI、G2Plot、Echarts等技术栈的仪表盘设计器&#xff0c;具备仪表盘目录管理…

Linux——线程条件变量(同步)

Linux——多线程的控制-CSDN博客 文章目录 目录 文章目录 前言 一、条件变量是什么&#xff1f; 1、死锁的必要条件 1. 互斥条件&#xff08;Mutual Exclusion&#xff09; 2. 请求和保持条件&#xff08;Hold and Wait&#xff09; 3. 不可剥夺条件&#xff08;No Preemption&…

“AI 辅助决策系统:决策路上的智慧领航员

在当今瞬息万变的时代&#xff0c;无论是企业的运营管理&#xff0c;还是个人在生活中的重大抉择&#xff0c;都需要精准、高效的决策。然而&#xff0c;信息的繁杂和未来的不确定性&#xff0c;常常让决策变得困难重重。这时&#xff0c;AI 辅助决策系统宛如一位智慧的领航员&…

某讯一面,感觉问Redis的难度不是很大

前不久&#xff0c;有位朋友去某讯面试&#xff0c;他说被问到了很多关于 Redis 的问题&#xff0c;比如为什么用 Redis 作为 MySQL 的缓存&#xff1f;Redis 中大量 key 集中过期怎么办&#xff1f;如何保证缓存和数据库数据的一致性&#xff1f;我将它们整理出来&#xff0c;…

DDD - 微服务落地的技术实践

文章目录 Pre概述如何发挥微服务的优势怎样提供微服务接口原则微服务的拆分与防腐层的设计 去中心化的数据管理数据关联查询的难题Case 1Case 2Case 3 总结 Pre DDD - 软件退化原因及案例分析 DDD - 如何运用 DDD 进行软件设计 DDD - 如何运用 DDD 进行数据库设计 DDD - 服…

闪豆多平台视频批量下载器

1. 视频链接获取与解析 首先&#xff0c;在哔哩哔哩网页中随意点击一个视频&#xff0c;比如你最近迷上了一个UP主的美食制作视频&#xff0c;想要下载下来慢慢学。点击视频后&#xff0c;复制视频页面的链接。复制完成后&#xff0c;不要急着关闭浏览器&#xff0c;因为接下来…

【STM32-学习笔记-14-】FLASH闪存

文章目录 FALSH闪存一、FLASH简介二、FLASH基本结构三、FLASH解锁四、使用指针访问存储器五、FLASH擦除以及编程流程Ⅰ、程序存储器全擦除1. 读取FLASH_CR的LOCK位2. 检查LOCK位是否为13. 设置FLASH_CR的MER 1和STRT 1&#xff08;如果LOCK位0&#xff09;4. 检查FLASH_SR的B…