springboot集成quartz,简版-通俗易懂

在网上查询资料,写写删删六七遍还是没怎么搞懂,最后还是写出来了,总结一下,确定是最简单的了。

前言

为什么要使用quartz,而不用springboot的@Scheduled?

因为在日常的业务中,需要经常变换任务的执行时间,即cron的表达式,所以对于@Scheduled用来操作就有些麻烦了,而quartz就很好的可以封装成一个可修改的调用接口,方便业务的多变性


springboot使用quartz执行任务


思路

为什么要说一下思路,因为这个执行的顺序把我搞得很懵,所以只需要将这个思路搞清楚,就很容易的使用quartz的功能了,我简单描述一下我的这个思路

  • 为业务层(eg: service)封装调用quartz的方法,该方法暴露执行的cron表达式(即第一步)

  • 将业务层需要执行的作业放到quartz的执行方法中(即第二步)

  • springboot集成quartz,将quartz任务调度器跟随项目启动而启动起来(即第三步)

    思路理顺了,业务的代码也就知道在哪里写了。


添加POM依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

1. 任务调度器

在业务层可以调用这个调度器,使用spring的注入@Autowired

package com.example.springboothtml.scheduler;

import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * 调度器使用
 *
 * @author jiaohongtao
 * @version 1.0
 * @since 2020年11月04日
 */
@Slf4j
@Component
public class QuartzScheduler {

    private static final String JOB_NAME = "inspect_report";
    private static final String JOB_GROUP = "inspect_report_group";
    private static final String TRIGGER_NAME = "inspect_report";
    private static final String TRIGGER_GROUP = "inspect_report_group";
    private static final String JOB_TASK_ID = "job_task_id";

    /**
     * quartz任务调度器
     */
    @Autowired
    private Scheduler scheduler;

    /**
     * 开始执行所有任务,并开启调度器
     *
     * @throws SchedulerException SchedulerException
     */
    public void startJob() throws SchedulerException {
    	// 这里可以放一些初始化的任务,例如服务器宕机后,需要重新启动,如果没有不用考虑这个
    	// 步骤:1.创建一个新的 SchedulerJob 作业类,即第二步的代码
    	// 2.在这个类里写一个方法(invoke())调用新SchedulerJob的作业,然后将方法放到这里
        scheduler.start();
    }

    public void add(int i, String cron) throws SchedulerException {
    	// 构建传递参数
    	JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put(JOB_TASK_ID, id);
        jobDataMap.put("userId", userId);
        
        JobDetail jobDetail = JobBuilder.newJob(InspectReportSchedulerJob.class).usingJobData(jobDataMap).
                withIdentity(JOB_NAME + id, JOB_GROUP).build();
        // 每5s执行一次
        // String cron = "*/5 * * * * ?";
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().
                withIdentity(TRIGGER_NAME + i, TRIGGER_GROUP).withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    public void remove(int i) throws SchedulerException {
        boolean deleteJob = scheduler.deleteJob(new JobKey(JOB_NAME + i, JOB_GROUP));
        log.info(deleteJob ? "任务移除成功" : "任务移除失败");
    }

    /**
     * 初始注入scheduler
     *
     * @return scheduler
     * @throws SchedulerException SchedulerException
     */
    @Bean
    public Scheduler scheduler() throws SchedulerException {
        SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
        return schedulerFactoryBean.getScheduler();
    }
}

2. 需要执行的业务任务

将需要执行的业务放在放在execute方法中

package com.example.springboothtml.scheduler;

import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;

/**
 * @author jiaohongtao
 * @version 1.0
 * @since 2020年11月04日
 */
@Slf4j
public class SchedulerJob implements Job {
    private void before() {
        System.out.println("任务开始执行");
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        before();
        log.info(new Date() + ":我开始执行了......");
        // TODO 业务
        after();
    }

    private void after() {
        System.out.println("任务结束执行");
    }
}

3. 启动项目时将quartz也启动

这个类是在项目启动时,将quartz的任务调度器也拉起来,即第一步的里的方法 — quartzScheduler.startJob()

package com.example.springboothtml.scheduler;

import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;

/**
 * 启动服务后,开启调度器
 *
 * @author jiaohongtao
 * @version 1.0
 * @since 2020年11月04日
 */
@Configuration
public class QuartzStartListener implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private QuartzScheduler quartzScheduler;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        try {
            quartzScheduler.startJob();
            System.out.println("*******quartz调度器启动*******");
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

cron表达式生成工具类

package com.bocloud.inspect.service.util;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * cron表达式生成工具类
 *
 * @author jiaohongtao
 * @version 1.0
 * @since 2020年11月04日
 */
public class CronUtil {

    /**
     * 生成指定格式日期字符
     *
     * @param date       日期
     * @param dateFormat : e.g:yyyy-MM-dd HH:mm:ss
     * @return formatTimeStr
     */
    public static String formatDateByPattern(Date date, String dateFormat) {
        dateFormat = dateFormat == null ? "yyyy-MM-dd HH:mm:ss" : dateFormat;
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        return date != null ? sdf.format(date) : null;
    }

    /**
     * 生成cron表达式 ss mm HH dd MM ? yyyy
     * convert Date to cron ,eg.  "0 06 10 15 1 ? 2014"
     *
     * @param date : 时间点
     */
    public static String getCron(Date date) {
        String dateFormat = "ss mm HH dd MM ? yyyy";
        return formatDateByPattern(date, dateFormat);
    }

    /**
     * 生成cron表达式 ss mm HH dd MM ?
     * convert Date to cron ,eg.  "0 06 10 15 1 ?"
     *
     * @param date : 时间点
     * @param type : 类型 日/周/月
     */
    public static String getLoopCron(Date date, String type, Integer week, Integer day) {
        String dateFormat = "ss mm HH";
        //  dd MM ?
        String cron = formatDateByPattern(date, dateFormat);
        switch (type) {
            case "Day":
                return cron + " * * ?";
            case "Week":
                return cron + " ? * " + getCurrentWeek(week);
            case "Month":
                return cron + " " + day + " * ?";
            default:
                return "false";
        }
    }


    /**
     * 获取当前星期的字符 MON TUE WED THU FRI SAT SUN
     *
     * @param week : 周 1 2 3 4 5 6 7
     * @return 星期字符
     */
    public static String getCurrentWeek(Integer week) {
        String[] weeks = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
        return weeks[week - 1];
    }

    public static void main(String[] args) {
        Date date = new Date();

        String dateFormat = "ss mm HH dd MM ? yyyy";
        String cron = formatDateByPattern(date, dateFormat);
        System.out.println("原始:" + cron);

        String day = formatDateByPattern(date, "ss mm HH");
        System.out.println("日报:" + day + " * * ?");

        // 0 15 10 ? * MON 每周一上午10点15分
        // 动参为 周
        String week = formatDateByPattern(date, "ss mm HH");
        System.out.println("周报:" + week + " ? * MON");

        // 0 15 9 10 * ? 每月10号9点15分
        // 动参为 号
        String month = formatDateByPattern(date, "ss mm HH");
        System.out.println("月报:" + month + " 10 * ?");
    }
}

测试代码

package com.example.springboothtml;

import com.example.springboothtml.scheduler.CronUtil;
import com.example.springboothtml.scheduler.QuartzScheduler;
import org.junit.Test;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author jiaohongtao
 * @version 1.0
 * @since 2020年11月04日
 */
@SpringBootTest
public class QuartzTest {

    @Autowired
    QuartzScheduler quartzScheduler;
    @Test
    public void test1() {
        // Date date = new Date();
        // String time = CronUtil.formatDateByPattern(date, null);
        Date date = null;
        try {
            date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2020-11-04 17:25:30");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        try {
            quartzScheduler.add(1, CronUtil.getCron(date));
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

如果有看不懂的部分,请在评论区留言
### 整合Spring Boot与Quartz实现数据库持久化的作业调度 #### 配置依赖项 为了使Spring Boot应用程序能够支持Quartz并利用`quartz-jobs`库来完成数据库中的作业存储,需先调整项目的构建文件。对于Maven项目,在`pom.xml`中加入如下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <!-- Quartz Jobs --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>${quartz.version}</version> </dependency> ``` 上述配置引入了必要的组件用于启动和管理定时任务以及通过JDBC连接到关系型数据库保存job详情[^1]。 #### 数据源设置 接着定义数据源属性以便于Quartz可以访问外部数据库执行持久操作。编辑`application.properties`或`application.yml`: ```yaml spring.datasource.url=jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=password spring.jpa.hibernate.ddl-auto=update ``` 这里指定了MySQL作为后台存储介质;当然也可以选择其他类型的RDBMS只要相应修改URL即可满足需求[^4]。 #### 初始化表结构 为了让Quartz能在指定的数据源上创建所需的表格,应该下载官方提供的SQL脚本并将之应用于目标schema内。通常这些DDL语句位于Quartz发行下的`docs/dbTables`目录下找到适合本的建表语句应用至实际环境中[^2]。 #### 自定义Job类 编写具体的业务逻辑单元即所谓的Jobs, 它们代表待被执行的任务体。下面给出一个单的例子展示怎样继承自`Job`接口从而形成可被调度器识别的对象形式: ```java import org.quartz.Job; import org.quartz.JobExecutionContext; public class SampleJob implements Job { @Override public void execute(JobExecutionContext context){ System.out.println("Executing sample job at " + new Date()); } } ``` 此段代码实现了基本的日志记录功能每当触发条件达成时就会打印当前时间戳信息给控制台输出流[^3]。 #### 调度策略设定 后一步就是安排何时何地运行已注册好的jobs了。这可以通过编程方式动态添加trigger实例或者静态声明的方式预先确定好计划参数。以下是采用Java Config风格的例子说明如何做到这一点: ```java @Configuration @EnableScheduling public class SchedulerConfig { @Autowired private ApplicationContext applicationContext; @Bean public JobDetailFactoryBean jobDetail(){ JobDetailFactoryBean factory = new JobDetailFactoryBean(); factory.setJobClass(SampleJob.class); factory.setDescription("Invoke SampleJob"); return factory; } @Bean public CronTriggerFactoryBean cronTrigger() throws ParseException{ CronTriggerFactoryBean trigger = new CronTriggerFactoryBean(); trigger.setCronExpression("*/5 * * * * ?"); //每五秒一次 trigger.setJobDetail(jobDetail().getObject()); return trigger; } } ``` 这段配置片段设置了每隔五分钟就激活一次之前提到过的SampleJob示例程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值