Java定时任务之Springboot整合Quartz(三)

一、添加依赖

springboot官方添加了Quartz的依赖,我们需要在pom文件中引入:

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

二、添加一个简单的任务

官方的以来直接只需要继承QuartzBean

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class MyTask extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("简单的定时任务执行时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }

}

添加配置类将任务添加到调度任务里面:

import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QuartzConfig {

    @Bean
    public JobDetail uploadTaskDetail() {
        return JobBuilder.newJob(MyTask.class)
                .withIdentity("MyTask")
                .storeDurably()
                .build();
    }

    // 设置触发器,并添加调度器
    @Bean
    public Trigger uploadTaskTrigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/5 * * * * ?");
        return TriggerBuilder.newTrigger()
                .forJob(uploadTaskDetail())
                .withIdentity("MyTask")
                .withSchedule(scheduleBuilder)
                .build();
    }
}

启动项目Quartz就可以执行:

这个的任务是写死的,效果太差,下面我们看一个动态任务如何使用!

三、数据源设置

添加JdbcTempatemysql相关依赖:

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

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

添加一个任务相关的表

CREATE TABLE `quartz` (
  `job_id` int(11) NOT NULL AUTO_INCREMENT,
  `job_name` varchar(50) DEFAULT NULL,
  `job_class` varchar(255) DEFAULT NULL,
  `job_status` int(11) DEFAULT NULL,
  `cron_expression` varchar(255) DEFAULT NULL,
  `can_view` bit(1) DEFAULT b'1',
  PRIMARY KEY (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

增加对应的实体类

@Data
public class QuartzEntity implements Serializable {
    /** 任务ID */
    private String jobId;
    /** 任务名称 */
    private String jobName;
    /** 任务运行类 */
    private String jobClass;
    /** 任务状态 */
    private Integer jobStatus;
    /** 任务运行时间表达式 */
    private String cronExpression;
    /** 是否删除 */
    private Boolean canView;
}

系统配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://192.168.31.26:3306/test_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
logging:
  level:
    # 显示jdbctemplate的sql语句
    org.springframework.jdbc.core.JdbcTemplate: DEBUG

四、增加一个任务

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
public class MyTask extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        log.info("线程[{}] - [{}]执行的开始任务:", Thread.currentThread().getName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        log.info("执行业务中......");
        try {
            Thread.sleep(10 * 1000);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
        log.info("线程[{}] - [{}]执行的结束任务:", Thread.currentThread().getName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }
}

五、定时任务的CRUD

先将JdbcTemplateScheduler注入

@Autowired
private JdbcTemplate jdbcTemplate;

@Autowired
private Scheduler scheduler;

接着添加一个公共方法:

/** 获取单个任务 */
private QuartzEntity getOne(Integer id) throws Exception {
    QuartzEntity quartzEntity = jdbcTemplate.queryForObject("select * from quartz where can_view = true and job_id = ?", new BeanPropertyRowMapper<>(QuartzEntity.class), id);
    if (Objects.isNull(quartzEntity)) {
        log.error("查询的数据不存在");
        throw new Exception("查询的数据不存在");
    }
    return quartzEntity;
}
5.1. 启动任务

启动单个任务:

public String start(Integer id) throws Exception {
    // 获取单个任务
    QuartzEntity quartzEntity = getOne(id);
    try {
        // 反射获取具体的任务
        Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzEntity.getJobClass());
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzEntity.getJobName()).build();
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzEntity.getCronExpression());
        // 设置触发器
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzEntity.getJobName()).withSchedule(scheduleBuilder).build();
        // 将具体任务和触发器加入到调度器
        scheduler.scheduleJob(jobDetail, trigger);
    } catch (ClassNotFoundException e) {
        log.warn("定时任务路径");
    } catch (SchedulerException e) {
        log.warn("创建定时任务出错:{}", e.getMessage());
    }
    return "success";
}
5.2. 更新任务
/**
 * 更新任务
 * @param quartzEntity
 * @return
 */
public String update(QuartzEntity quartzEntity) {
    try {
        TriggerKey triggerKey = TriggerKey.triggerKey(quartzEntity.getJobName());
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzEntity.getCronExpression());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        scheduler.rescheduleJob(triggerKey, trigger);
    } catch (SchedulerException e) {
        log.error("更新定时任务出错:{}", e.getMessage());
    }
    return "更新定时任务成功";
}
5.3. 暂停任务
/**
 * 暂停任务
 * @param id
 * @return
 */
public String stop(Integer id) throws Exception {
    // 获取某一个任务实体
    QuartzEntity quartzEntity = getOne(id);
    // 通过任务名获取任务
    JobKey jobKey = JobKey.jobKey(quartzEntity.getJobName());
    try {
        // 暂停任务
        scheduler.pauseJob(jobKey);
    } catch (SchedulerException e) {
        log.error("暂定定时任务出错:{}", e.getMessage());
    }
    return "任务暂停";
}
5.4. 恢复任务
/**
 * 暂停任务
 * @param id
 * @return
 * @throws Exception
 */
public String resume(Integer id) throws Exception {
    QuartzEntity quartzEntity = getOne(id);
    JobKey jobKey = JobKey.jobKey(quartzEntity.getJobName());
    try {
        scheduler.resumeJob(jobKey);
    } catch (SchedulerException e) {
        log.error("暂定定时任务出错:{}", e.getMessage());
    }
    return "恢复任务成功";
}
5.5. 删除任务
/**
 * 删除任务
 * @param id
 * @return
 * @throws Exception
 */
public String delete(Integer id) throws Exception {
    QuartzEntity quartzEntity = getOne(id);
    jdbcTemplate.update("update quartz set can_view = false where job_id = ?", quartzEntity.getJobId());
    JobKey jobKey = JobKey.jobKey(quartzEntity.getJobName());
    try {
        scheduler.deleteJob(jobKey);
    } catch (SchedulerException e) {
        log.error("删除定时任务出错:{}", e.getMessage());
    }
    return "恢复任务成功";
}

六、持久化

6.1. 系统配置文件

我们上面是使用的是内存的持久化,如果上次没有运行完,下次重启任务就丢失了!所以下面看一下如何持久化到数据库,需要在数据库上就行操作(这样我们就可以不需要我们自定义存储任务的表了):

spring:
  quartz:
    # 数据库方式
    job-store-type: jdbc
    jdbc:
      # 第一次运行时候会初始化数据表
      initialize-schema: always
    properties:
      org:
        quartz:
          scheduler:
            # 调度器的实例名称
            instanceName: clusteresScheduler
            # 设置调度器的实例ID (instanceId)如果使用集群,instanceId必须唯一,设置成AUTO
            instanceId: AUTO
          jobStore:
            # 事务配置 先执行业务逻辑在启动任务,保证业务出错之后可以回滚
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            # 驱动器方言 数据库平台
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            # 表前缀
            tablePrefix: QRTZ_
            # 集群模式
            isClustered: auto
          # 连接池配置
          threadPool:
            # 连接池类
            class: org.quartz.simpl.SimpleThreadPool
            # 总线程
            threadCount: 10
            # 核心线程
            threadPriority: 5
            # 线程继承初始化线程的上下文类加载器
            threadsInheritContextClassLoaderOfInitializingThread: true

上面使用的是我们项目的数据源,我们也可以自定义quartz数据源(很多时候需要将quartz数据源和业务数据源分类的):

spring:
  quartz:
    # ......
    properties:
      org:
        quartz:
          jobStore:
            # ......
            # Quartz自定义数据源配置
            dataSource: quartzDataSource
          # 自定义的数据源配置
          dataSource:
            quartzDataSource:
              driver: com.mysql.cj.jdbc.Driver
              url: jdbc:mysql://192.168.31.26:3306/test_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
              user: root
              password: 123456

项目启动之后生成的数据表(在org.quartz-scheduler:quartz:2.3.2org.quartz.impl.jdbcjobstore下面我们可以看到初始化的sql。):

6.2. 持久化的表
表明作用
QRTZ_FIRED_TRIGGERS激发的触发器
QRTZ_PAUSED_TRIGGER_GRPS暂停的触发器
QRTZ_SCHEDULER_STATE存储集群中note实例信息,quartz会定时读取该表的信息判断集群中每个实例的当前状态
QRTZ_CRON_TRIGGERS存储 CronTrigger,包括 Cron表达式和时区信息
QRTZ_LOCKS存储程序的悲观锁的信息(假如使用了悲观锁)
QRTZ_JOB_DETAILS存储每一个已配置的 Job 的详细信息
QRTZ_TRIGGERS存储已配置的 Trigger 的信息
QRTZ_SIMPROP_TRIGGERS
QRTZ_SIMPLE_TRIGGERS存储简单的Trigger,包括重复次数,间隔,以及已触的次数
QRTZ_BLOB_TRIGGERS作为 Blob 类型存储(用于 Quartz 用户用 JDBC创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
QRTZ_CALENDARS存放日历信息, quartz可配置一个日历来指定一个时间范围

我们使用的是CronTrigger触发器,我们用到的表只有:QRTZ_CRON_TRIGGERS, QRTZ_FIRED_TRIGGERS, QRTZ_JOB_DETAILS, QRTZ_TRIGGERS, QRTZ_LOCKS

6.3. 定时任务CRUD
6.3.1. 获取所有计划中的任务列表
/**
 * 获取所有计划中的任务列表
 * @return
 */
public List<Map<String, Object>> list() throws Exception {
    List<Map<String, Object>> jobList = new ArrayList<>();
    try {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                Map<String, Object> temp = new HashMap<>();
                temp.put("jobName", jobKey.getName());
                temp.put("jobGroupName", jobKey.getGroup());
                temp.put("description", "触发器:" + trigger.getKey());
                Trigger.TriggerState state = scheduler.getTriggerState(trigger.getKey());
                temp.put("status", state.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    temp.put("jobName", cronExpression);
                }
                jobList.add(temp);
            }
        }
    } catch (SchedulerException e) {
        throw new Exception("获取任务列表失败");
    }
    return jobList;
}
6.3.2. 获取所有正在运行的任务
/**
 * 获取正在运行的List
 * @return
 */
public List<Map<String, Object>> runList() throws Exception {
    List<Map<String, Object>> jobList = new ArrayList<>();
    try {
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        for (JobExecutionContext jobExecutionContext : executingJobs) {
            Map<String, Object> temp = new HashMap<>();
            JobDetail jobDetail = jobExecutionContext.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = jobExecutionContext.getTrigger();
            temp.put("jobName", jobKey.getName());
            temp.put("groupName", jobKey.getGroup());
            temp.put("description", "触发器" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            temp.put("status", triggerState.name());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                temp.put("jobName", cronExpression);
            }
            jobList.add(temp);
        }
    } catch (SchedulerException e) {
        throw new Exception("获取任务列表失败");
    }
    return jobList;
}
6.3.3. 立即执行一个Job
/**
 * 立即执行job
 * @param jobName
 * @param groupName
 * @return
 */
public void onceRunJob(String jobName, String groupName) throws Exception {
    try {
        JobKey jobKey = JobKey.jobKey(jobName, groupName);
        scheduler.triggerJob(jobKey);
    } catch (SchedulerException e) {
        throw new Exception("立即执行Job失败");
    }
}
6.3.4. 恢复任务
/**
 * 恢复执行Job
 * @param jobName
 * @param groupName
 * @throws Exception
 */
public void resumeJob(String jobName, String groupName) throws Exception {
    try {
        JobKey jobKey = JobKey.jobKey(jobName, groupName);
        scheduler.resumeJob(jobKey);
    } catch (SchedulerException e) {
        throw new Exception("恢复执行Job失败");
    }
}
6.3.5. 暂停任务
/**
 * 暂停执行Job
 * @param jobName
 * @param groupName
 * @throws Exception
 */
public void pauseJob(String jobName, String groupName) throws Exception {
    try {
        JobKey jobKey = JobKey.jobKey(jobName, groupName);
        scheduler.pauseJob(jobKey);
    } catch (SchedulerException e) {
        throw new Exception("暂停执行Job失败");
    }
}
6.3.6. 删除任务
/**
 * 删除Job
 * @param jobName
 * @param groupName
 * @throws Exception
 */
public void deleteJob(String jobName, String groupName) throws Exception {
    try {
        JobKey jobKey = JobKey.jobKey(jobName, groupName);
        scheduler.deleteJob(jobKey);
    } catch (SchedulerException e) {
        throw new Exception("暂停执行Job失败");
    }
}
6.3.7. 修改任务
/**
 * 更新执行Job
 * @param jobName
 * @param groupName
 * @param jobTime
 * @throws Exception
 */
public void updateJob(String jobName, String groupName, String jobTime) throws Exception {
    try {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
            .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
        // 重启触发器
        scheduler.rescheduleJob(triggerKey, trigger);
    } catch (SchedulerException e) {
        throw new Exception("更新执行Job失败");
    }
}
6.3.8. 增加任务
/**
 * 增加一个job
 * @param cronJobParam
 */
public void addJob(CronJobParam cronJobParam) {
    try {
        // 创建jobDetail实例,绑定Job实现类
        // 指明job的名称,所在组的名称,以及绑定job类
        // 任务名称和组构成任务key
        Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(cronJobParam.getJobClass());
        JobDetail jobDetail = JobBuilder.newJob(jobClass)
            .withIdentity(cronJobParam.getJobName(), cronJobParam.getGroupName())
            .build();
        // 设置job参数
        if(cronJobParam.getJobData() != null && cronJobParam.getJobData().size() > 0){
            jobDetail.getJobDataMap().putAll(cronJobParam.getJobData());
        }
        // 定义调度触发规则
        // 使用cornTrigger规则
        // 触发器key
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(cronJobParam.getJobName(), cronJobParam.getGroupName())
            .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
            .withSchedule(CronScheduleBuilder.cronSchedule(cronJobParam.getJobTime()))
            .startNow()
            .build();
        // 把作业和触发器注册到任务调度中
        scheduler.scheduleJob(jobDetail, trigger);
    } catch (Exception e) {
        e.printStackTrace();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值