SpringBoot定时器和quartz

1 介绍

Spring Boot内置了一个简单的定时任务调度功能,通过@Scheduled注解就可以实现定时任务。这个内置的定时器对于一些简单的定时任务需求是足够的,例如定时发送邮件、定时清理缓存等。

但是,当你的定时任务需求变得更加复杂,需要更多的调度策略和灵活性时,Spring Boot内置的定时器可能就无法满足你的要求。这时候,Quartz作为一个专业的调度框架就发挥了作用。

Quartz相较于Spring Boot内置的定时器有以下优势:

  • 更丰富的调度策略:Quartz支持多种调度策略,如SimpleTrigger(简单触发器)、CronTrigger(Cron表达式触发器)等,可以灵活地定义任务的执行时间规则。
  • 集群支持:Quartz能够实现集群部署,多个节点共享同一个数据库,保证定时任务在集群环境下的稳定和高可用性。
  • 持久化存储:Quartz支持将定时任务的信息持久化到数据库中,这样即使应用程序重启或崩溃,定时任务的信息不会丢失。
  • 错过触发处理:Quartz能够处理错过触发的任务,例如当应用程序暂停或关闭时,任务原本应该执行但未执行的情况。
  • 并发控制:Quartz支持对任务的并发执行进行控制,例如可以设定任务只能在单个线程中执行,避免出现并发问题。

总的来说,SpringBoot内置的定时器适用于简单的定时任务场景,而Quartz则提供了更强大、更灵活的功能,适用于复杂的定时任务调度场景,特别是在需要精确控制任务执行时间、高可用性和并发控制等方面。所以,如果你的项目需要更多的定时任务调度特性,那么Quartz会是一个更好的选择。

1.1 内置定时器

定时器是一种控制任务延时调用,或者周期调用的技术。
作用:定时邮件、短信发送、更新数据、同步数据、检查数据库和缓存数据是否一致、将定时任务限制在每天的工作时间等。

首先要有web的依赖

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

1.2 quartz

依赖

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

配置

spring:
  quartz:
    # 持久化到数据库方式
    job-store-type: jdbc
    # 初始化表结构
    initialize-schema: embedded

    properties:
      org:
        quartz:
          # 调度器实例的名称
          scheduler:
            instanceName: MyScheduler
            # 调度器实例的ID,使用AUTO表示自动生成
            instanceId: AUTO

          # JobStore相关配置
          jobStore:
            # 是否在获得锁之后获取触发器
            acquireTriggersWithinLock: true
            # JobStore的实现类
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            # JobStore的驱动代理类
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            # 数据库表名前缀
            tablePrefix: QRTZ_
            # 是否开启集群模式
            isClustered: true
            # 集群节点心跳检查间隔(毫秒)
            clusterCheckinInterval: 10000
            # 是否使用属性存储触发器的属性信息
            useProperties: false

          # 线程池配置
          threadPool:
            # 线程池的实现类
            class: org.quartz.simpl.SimpleThreadPool
            # 线程池的线程数
            threadCount: 10
            # 线程池的优先级
            threadPriority: 5
            # 线程池的线程是否继承初始化线程的ClassLoader
            threadsInheritContextClassLoaderOfInitializingThread: true

# 定义定时任务触发器的配置
quartz:
  schedules:
    - job-name: cleanupJob
      cron-expression: 0 0 1 * * ? # 每天凌晨1点执行

建表
但是不建表也能使用

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,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(190) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
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))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
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))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
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))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    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))
ENGINE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(190) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(190) NULL,
JOB_GROUP VARCHAR(190) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

2 注解

注解作用位置
@EnableScheduling开启定时器启动类上,或者定时器上
@Scheduled定时器任务,并指定cron表达式定时器方法上

3 代码

3.1内置定时器

package com.example.springjpaquery.schedule;

import cn.hutool.core.date.DateUtil;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 定时器测试类
 * @version 1.0.0
 * @date 2022/10/20
 */
@Component
@EnableScheduling
@EnableAsync // 异步注解 @EnableAsync+@Async配合使用,这里只是简单记录
public class TestSchedule {
    /**
     * 每10秒执行一次
     */
    @Scheduled(cron = "0/10 * * * * ?")
    @Async
    public void handler() {
        System.out.println(DateUtil.now());
    }
}

在这里插入图片描述

3.2 quartz

需求:假设你正在开发一个在线学习平台,需要实现以下定时任务:

每天凌晨1点,统计当天新增用户数量,并发送邮件通知管理员。
每周一凌晨2点,计算上一周的学习活跃用户,将活跃用户信息保存到数据库。

@Component
public class ActiveUserStatsJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 在这里编写计算活跃用户信息的逻辑
        System.out.println("计算学习活跃用户并保存到数据库...");
    }
}
@Component
public class NewUserStatsJob implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 在这里编写统计新增用户的逻辑
        System.out.println("统计新增用户数量并发送邮件通知管理员...");
    }
}
/**
 * quartz配置类
 * 注意: 添加后运行程序即可quartz自动运行
 * @version 1.0.0
 * @date 2023/8/3
 */
@Configuration
public class QuartzConfig {

    @Bean
    public JobDetail newUserStatsJobDetail() {
        return newJob(NewUserStatsJob.class)
                .withIdentity("newUserStatsJob")
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger newUserStatsJobTrigger() {
        return newTrigger()
                .forJob(newUserStatsJobDetail())
                .withIdentity("newUserStatsJobTrigger")
//                .withSchedule(cronSchedule("0 0 1 * * ?")) // 每天凌晨1点执行
                .withSchedule(cronSchedule("0 0/3 * * * ?")) // 3分钟
                .build();
    }

    @Bean
    public JobDetail activeUserStatsJobDetail() {
        return newJob(ActiveUserStatsJob.class)
                .withIdentity("activeUserStatsJob")
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger activeUserStatsJobTrigger() {
        return newTrigger()
                .forJob(activeUserStatsJobDetail())
                .withIdentity("activeUserStatsJobTrigger")
//                .withSchedule(cronSchedule("0 0 2 * * MON")) // 每周一凌晨2点执行
                .withSchedule(cronSchedule("0 0/2 * * * ?")) // 两分钟执行
                .build();
    }
}

在这里插入图片描述

3.3 问题

在这里插入图片描述

2023-09-04 09:47:34.163  WARN 22888 --- [           main] org.quartz.impl.jdbcjobstore.JobStoreTX  : Database connection shutdown unsuccessful.

java.sql.SQLException: There is no DataSource named 'null'
	at org.quartz.utils.DBConnectionManager.shutdown(DBConnectionManager.java:135) ~[quartz-2.3.2.jar:na]
	at org.quartz.impl.jdbcjobstore.JobStoreSupport.shutdown(JobStoreSupport.java:746) ~[quartz-2.3.2.jar:na]
	at org.quartz.core.QuartzScheduler.shutdown(QuartzScheduler.java:732) [quartz-2.3.2.jar:na]
	at org.quartz.impl.StdSchedulerFactory.shutdownFromInstantiateException(StdSchedulerFactory.java:1431) [quartz-2.3.2.jar:na]
	at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1391) [quartz-2.3.2.jar:na]
	at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1579) [quartz-2.3.2.jar:na]
	at org.springframework.scheduling.quartz.SchedulerFactoryBean.createScheduler(SchedulerFactoryBean.java:679) [spring-context-support-5.3.20.jar:5.3.20]
	at org.springframework.scheduling.quartz.SchedulerFactoryBean.prepareScheduler(SchedulerFactoryBean.java:616) [spring-context-support-5.3.20.jar:5.3.20]
	at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:504) [spring-context-support-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) [spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) [spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) [spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) [spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) [spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) [spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) [spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:934) ~[spring-beans-5.3.20.jar:5.3.20]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.20.jar:5.3.20]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.20.jar:5.3.20]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.0.jar:2.7.0]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[spring-boot-2.7.0.jar:2.7.0]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.0.jar:2.7.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.0.jar:2.7.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar:2.7.0]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar:2.7.0]
	at com.example.demo.DemoApplication.main(DemoApplication.java:16) ~[classes/:na]

原因:quartz报错是由于SpringBoot在2.5.6版本之后就删除了关于Quartz相关的依赖。所以在2.5.6版及之前还是可以用的。

解决方案:

  1. 降低版本
  2. 将配置文件中的org.quartz.impl.jdbcjobstore.JobStoreTX改为org.springframework.scheduling.quartz.LocalDataSourceJobStore

4 Cron表达式

1. 格式

七部分组成,其格式为:

秒 分 时 日 月 周 年(可为空)

2. 取值

位置介绍取值
10-59
20-59
30-24
41-31
51-12
6星期1-7
7可为空,1970-2099,若为空,表示全部时间范围

3. 特殊字符

字符解释
*每的意思。在不同的字段上,就代表每秒,每分,每小时等。如果在“日”这个域中设置 *,表示每一天都会触发。
-指定值的范围。这个比较好理解就是指定在某个域的连续范围,如果我们在 “时” 这个域中定义 1-6,则表示在1到6点之间每小时都触发一次,用 , 表示 1,2,3,4,5,6
,这里指的是在两个以上的时间点中都执行,如果我们在 “分” 这个域中定义为 8,12,35 ,则表示分别在第8分,第12分 第35分执行该定时任务。
/在某个域上周期性触发,该符号将其所在域中的表达式分为两个部分,其中第一部分是起始值,除了秒以外都会降低一个单位,比如 在 “秒” 上定义 5/10 表示从 第 5 秒开始 每 10 秒执行一次,而在 “分” 上则表示从 第 5 秒开始 每 10 分钟执行一次。
?仅用于【日】和【周】字段。因为在指定某日和周几的时候,这两个值实际上是冲突的,所以需要用【?】标识不生效的字段。比如【0 1 * * * ?】就代表每年每月每日每小时的1分0秒触发任务。这里的周就没有效果了。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的8号触发一个操作,但不关心是周几,我们可以这么设置 0 0 0 8 * ?
L表示英文中的LAST 的意思,只能在 “日”和“周”中使用。在“日”中设置,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年), 在“周”上表示周六,相当于”7”或”SAT”。如果在”L”前加上数字,则表示该数据的最后一个。例如在“周”上设置”7L”这样的格式,则表示“本月最后一个周六”
W表示离指定日期的最近那个工作日(周一至周五)触发,只能在 “日” 中使用且只能用在具体的数字之后。若在“日”上置”15W”,表示离每月15号最近的那个工作日触发。假如15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果是 “1W” 就只能往本月的下一个最近的工作日推不能跨月往上一个月推。
#表示每月的第几个周几,只能作用于 “周” 上。例如 ”2#3” 表示在每月的第三个周二。

4. 经典案例


"30 * * * * ?" 每半分钟触发任务
"30 10 * * * ?" 每小时的1030秒触发任务
"30 10 1 * * ?" 每天11030秒触发任务
"30 10 1 20 * ?" 每月2011030秒触发任务
"30 10 1 20 10 ? *" 每年102011030秒触发任务
"30 10 1 20 10 ? 2011" 2011102011030秒触发任务
"30 10 1 ? 10 * 2011" 201110月每天11030秒触发任务
"30 10 1 ? 10 SUN 2011" 201110月每周日11030秒触发任务
"15,30,45 * * * * ?"15秒,30秒,45秒时触发任务
"15-45 * * * * ?" 1545秒内,每秒都触发任务
"15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次
"15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
"0 0/3 * * * ?" 每小时的第00秒开始,每三分钟触发一次
"0 15 10 ? * MON-FRI" 星期一到星期五的10150秒触发任务
"0 15 10 L * ?" 每个月最后一天的10150秒触发任务
"0 15 10 LW * ?" 每个月最后一个工作日的10150秒触发任务
"0 15 10 ? * 5L" 每个月最后一个星期四的10150秒触发任务
"0 15 10 ? * 5#3" 每个月第三周的星期四的10150秒触发任务
"0 0 10,14,16 * * ?" 每天上午10点,下午2点,4"0 0/30 9-17 * * ?" 朝九晚五工作时间内每半小时
"0 0 12 ? * WED" 表示每个星期三中午12"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:102:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
"/5 * * * * ?" 每隔5秒执行一次
"0 */1 * * * ?" 每隔1分钟执行一次
"0 0 23 * * ?" 每天23点执行一次
"0 0 1 * * ?" 每天凌晨1点执行一次
"0 0 1 1 * ?" 每月1号凌晨1点执行一次
"0 0 23 L * ?" 每月最后一天23点执行一次
"0 0 1 ? * L" 每周星期天凌晨1点实行一次
"0 26,29,33 * * * ?"26分、29分、33分执行一次
"0 0 0,13,18,21 * * ?" 每天的0点、13点、18点、21点都执行一次
"0 0 3 ? * L" 每周周六凌晨3点实行一次
"0 24,30 * * * ?"24分、30分执行一次

若值不合法,调度器将抛出SchedulerException异常

5 Cron既然那么麻烦就生成吧

嘎嘎好用https://cron.qqe2.com/

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值