定时任务——quartz

基本使用

官网:http://www.quartz-scheduler.org/

先理解quartz的模型关系
  一个调度器可以调度多个触发器
  一个触发器只可以触发一个任务详情
  一个任务详情可以被多个触发器调用
  一个Job可以被多个任务详情包含(一般情况下不会用到这种场景)

  一般会定义两种触发器,一种是自动触发器,一种是手动触发器


跟着官网文档走,导入数据库,然后就可以进行案例实践了
这里引入一个logback配置,方便查看日志信息

<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="false" scan="false">
    <!--  Console log output  -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!--  WARN级别  -->
    <root level="WARN">
        <appender-ref ref = "console"/>
    </root>
</configuration>

官网上的Demo

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.util.Date;
import java.util.StringJoiner;

/**
 * Author: gzy
 * Date: 2022/11/22-10:52
 * Description:
 *  quartz中的job都要实现Job接口
 *  quartz所有的定时任务都要实现Job接口
 */
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        StringJoiner outStr = new StringJoiner(" ")
                .add("Hello.execute")
                .add(new Date().toString())
                .add("======" + Thread.currentThread().getName())
                .add(jobExecutionContext.getTrigger().getKey().getName());        // 触发器的名称
        // System.out.println("HelloJobExecute: " + new Date() + "======" + Thread.currentThread().getName());
        System.out.println(outStr);
    }
}
/**
 * Author: gzy
 * Date: 2022/11/22-10:41
 * Description:
 *  调度器 ->>> 触发器 ->>> 任务详情(任务)
 *      为什么中间有一层触发器
 */
public class QuartzTest {

    public static void main(String[] args) {

        try {
            // Grab the Scheduler instance from the Factory
            // 调度器工厂获取一个默认的调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // and start it off
            // 启动
            scheduler.start();

            // define the job and tie it to our HelloJob class
            //
            JobDetail job = newJob(HelloJob.class)
                    .withIdentity("job1", "group1")
                    .build();

            // Trigger the job to run now, and then repeat every 40 seconds
            // 触发 jobDetail 触发器
            Trigger trigger = newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()         // 启动
                    .withSchedule(simpleSchedule()          // 调度策略
                            .withIntervalInSeconds(5)      // 5s一次
                            .repeatForever())               // 永远循环下去
                    .build();

            // Tell quartz to schedule the job using our trigger
            // job 被 调度器调度
            scheduler.scheduleJob(job, trigger);

            // 主线程先睡一下, 以看到程序的效果
            TimeUnit.SECONDS.sleep(20);

            // 关闭
            scheduler.shutdown();

        } catch (SchedulerException | InterruptedException se) {
            se.printStackTrace();
        }
    }
}

接下来的案例验证调度器、触发器、作业详情之间的关系
两个触发器调用一个JobDetail

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import java.util.concurrent.TimeUnit;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Author: KongXiao
 * Date: 2022/11/22-10:41
 * Description:
 *  调度器 ->>> 触发器 ->>> 任务详情(任务)
 *      问什么中间有一层触发器
 *          一对多的关系 一个调度器 多个触发器;    多个触发器 一个任务详情
 *          一个触发器只能调度一个任务
 (          一个 Job 可以被多个 JobDetail 引用
 */
public class QuartzTest2 {

    public static void main(String[] args) {

        try {
            // Grab the Scheduler instance from the Factory
            // 调度器工厂获取一个默认的调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // and start it off
            // 启动
            scheduler.start();

            // define the job and tie it to our HelloJob class
            //
            JobDetail job = newJob(HelloJob.class)
                    .withIdentity("job1", "group1")
                    .build();

            // Trigger the job to run now, and then repeat every 40 seconds
            // 触发 jobDetail 触发器
            Trigger trigger = newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(1)
                            .repeatForever())
                    .build();

            Trigger trigger2 = newTrigger()
                    .withIdentity("trigger2", "group1")
                    .startNow()         // 启动
                    .forJob("job1", "group1")       // 指定 Job name 和 group,指定要触发的 job
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(3)
                            .repeatForever())
                    .build();


            // Tell quartz to schedule the job using our trigger
            // job 被 调度器调度
            scheduler.scheduleJob(job, trigger);
            scheduler.scheduleJob(trigger2);        // 如果是一个未被调度过的Job,使用 forJob的方式,然后调度器想要调度它,但是只传了一个触发器,会出异常

            // 主线程先睡一下
            TimeUnit.SECONDS.sleep(3);

            // 关闭
            scheduler.shutdown();

        } catch (SchedulerException | InterruptedException se) {
            se.printStackTrace();
        }
    }
}

两个调度器调用两个作业详情

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import java.util.concurrent.TimeUnit;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Author: KongXiao
 * Date: 2022/11/22-10:41
 * Description:
 *  调度器 ->>> 触发器 ->>> 任务详情(任务)
 *      问什么中间有一层触发器
 *          一对多的关系 一个调度器 多个触发器;    多个触发器 一个任务详情
 *          一个触发器只能调度一个任务
 *          一个 Job 可以被多个 JobDetail 引用
 *
 *  一个调度器对应俩触发器,一个触发器对应一个jobDetail,一个jobDetail可以被多个触发器调用,一个JobDetail包含一个Job
 */
public class QuartzTest3 {

    public static void main(String[] args) {

        try {
            // Grab the Scheduler instance from the Factory
            // 调度器工厂获取一个默认的调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // and start it off
            // 启动
            scheduler.start();

            // define the job and tie it to our HelloJob class
            //
            JobDetail job = newJob(HelloJob.class)
                    .withIdentity("job1", "group1")
                    .build();

            JobDetail job2 = newJob(HelloJob.class)         // 两个JobDetail包含了一个Job
                    .withIdentity("job2", "group1")
                    .build();

            // Trigger the job to run now, and then repeat every 40 seconds
            // 触发 jobDetail 触发器
            Trigger trigger = newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(1)
                            .repeatForever())
                    .build();

            Trigger trigger2 = newTrigger()
                    .withIdentity("trigger2", "group1")
                    .startNow()         // 启动
                    .forJob("job2", "group1")       // 指定 Job name 和 group,指定要触发的 job
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(3)
                            .repeatForever())
                    .build();


            // Tell quartz to schedule the job using our trigger
            // job 被 调度器调度
            scheduler.scheduleJob(job, trigger);
            scheduler.scheduleJob(job2, trigger2);      // 如果只传入一个触发器,会出异常
            // scheduler.scheduleJob(trigger2);   // org.quartz.JobPersistenceException: The job (group1.job2) referenced by the trigger does not exist.

            // 主线程先睡一下
            TimeUnit.SECONDS.sleep(3);

            // 关闭
            scheduler.shutdown();

        } catch (SchedulerException | InterruptedException se) {
            se.printStackTrace();
        }
    }
}

简单理解一下JobDetail和Trigger的name属性和group属性

name属性主要是用来方便标识的
group属性主要用来方便管理和标识的
多个作业可以放在一个群组下面进行管理
可以不为group赋值,但是底层会为其赋一个 DEFAULT 值

只有一个JobDetail和一个Trigger时,可以不指定name和group,如下
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import java.util.concurrent.TimeUnit;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Author: KongXiao
 * Date: 2022/11/22-10:41
 * Description:
 *  调度器 ->>> 触发器 ->>> 任务详情(任务)
 *      问什么中间有一层触发器
 *          一对多的关系 一个调度器 多个触发器;    多个触发器 一个任务详情  一个触发器只能调度一个任务
 *          一个 Job 可以被多个 JobDetail 引用
 *
 *
 *  JobDetail和Trigger的name和group的作用:
 *      name是用来标记的
 *      group是用来标记、方便管理的(比如,一个群组下也可以有多个job)
 *      可以不为 group赋值,那么 group底层会赋 DEFAULT 值
 *
 *      如果只有一个触发器、一个JobDetail,那么可以不指定name和group,可以试一试, 那么此时线程名是MD5加密的串
 *
 *
 *      那么现在的模型大概是这样的
 *          一个调度器可以调用多个触发器
 *          一个触发器只可以调用一个任务详情
 *          一个任务详情可以被多个触发器触发
 *          一个任务可以被多个任务详情包含
 *
 *          一般情况下,不会使用多个JobDetail去包含一个Job的场景,会编写两种触发器,一个自动触发器,一个手动触发器.
 */
public class QuartzTest4 {

    public static void main(String[] args) {

        try {
            // Grab the Scheduler instance from the Factory
            // 调度器工厂获取一个默认的调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // and start it off
            // 启动
            scheduler.start();

            // define the job and tie it to our HelloJob class
            //
            JobDetail job = newJob(HelloJob.class)
                    // .withIdentity("job1", "group1")
                    .build();


            // Trigger the job to run now, and then repeat every 40 seconds
            // 触发 jobDetail 触发器
            Trigger trigger = newTrigger()
                   // .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(1)
                            // .repeatForever())
                            .withRepeatCount(1))            // 指定重复执行的次数
                    .build();



            // Tell quartz to schedule the job using our trigger
            // job 被 调度器调度
            scheduler.scheduleJob(job, trigger);

            // 主线程先睡一下
            TimeUnit.SECONDS.sleep(3);

            // 关闭
            scheduler.shutdown();

        } catch (SchedulerException | InterruptedException se) {
            se.printStackTrace();
        }
    }
}

几个触发器

CalendarIntervalTriggerImpl (org.quartz.impl.triggers)
SimpleTriggerImpl (org.quartz.impl.triggers)
DailyTimeIntervalTriggerImpl (org.quartz.impl.triggers)
CronTriggerImpl (org.quartz.impl.triggers)  重点、常用!!!

CronExpression cron表达式

Cron表达式生成网站 https://cron.qqe2.com/
...

传参以及依赖注入

Job的需要一个无参构造方法才可以被实例化,Job的实例化不是我们创建的,是quartz去帮我们实现的

传入一个Job.class到newJob中可以创建出一个Job的实例

在Job实例化的时候传参可以从JobDetail和Trigger下手,调用usingJobData()方法,底层是个Map。在Job中取值,可以从Job上下文中调用 getJobDetail()/getTrigger().getJobDataMap().get(key);
getMergedJobDataMap()可以获取合并后的数据,如果JobDetail和Trigger的key相同,那么会以Trigger的key优先,获取到的就是Trigger相应的值

Job中定义一个变量并定义相应的set方法,也可以获取值

如果在Job中注入Spring中的Bean该怎么操作呢?
Spring的Bean是由Spring管理的,而quartz是不认这个的,所以直接在Job中注入依赖是获取不到值的
@Component
public class SpringDIUtils implements ApplicationContextAware {
    
    public static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

Job中获取值
xxx = (xxx) SpringDIUtils.applicationContext.getBean(StringUtils.uncapitalize(xxx.class.getSimpleName()));

quartz的配置文件
quartz的配置文件是quartz.properties,如果不自行配置的话会默认使用自带的配置文件, 在 quartz的jar包的 org.quartz包下

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
posted @   gzyddd  阅读(547)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示