簡單的任務可用 spring task scheduler,複雜的要改用 quartz
pom.xml 要加上 spring-boot-starter-quartz
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
自動設定類別是 QuartzAutoConfiguration,透過註冊的 SchedulerFactoryBean 就能產生 Scheduler。參數綁定類別為 QuartzProperties,參數為 spring.quartz.*
產生 task
只要繼承 QuartzJobBean,實作 executeInternal,就可以產生 quartz 的 Job
SimpleTask.java
package com.test.quartz;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
@Slf4j
public class SimpleTask extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) {
log.info("simple task");
}
}
設定
JobDetail
Calendar: 指定/排除特定時間
Trigger
TaskConfig.java
package com.test.quartz;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@RequiredArgsConstructor
@Configuration
public class TaskConfig {
public static final String SIMPLE_TASK = "simple-task";
// private final SchedulerFactoryBean schedulerFactoryBean;
//
// @PostConstruct
// public void init() throws SchedulerException {
// Scheduler scheduler = schedulerFactoryBean.getScheduler();
// boolean exists = scheduler.checkExists(JobKey.jobKey(SIMPLE_TASK));
// if (!exists) {
// scheduler.scheduleJob(simpleTask(), simpleTaskTrigger());
// }
// }
@Bean
public JobDetail simpleTask() {
return JobBuilder.newJob(SimpleTask.class)
.withIdentity(SIMPLE_TASK)
.withDescription("simple-task")
.storeDurably()
.build();
}
@Bean
public Trigger simpleTaskTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/3 * * * * ? *");
return TriggerBuilder.newTrigger()
.withIdentity("simple-task-trigger")
.forJob(simpleTask())
.withSchedule(cronScheduleBuilder)
.build();
}
}
cron expression 多了最後一個 [<year>]
可以不填寫
<second> <minute> <hour> <day of month> <month> <day of week> [<year>]
ex:
"0/3 * * * * ? 2025-2030"
代表 2025-2030 每 3s 執行一次
啟動後,會看到
org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) 'quartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
設定參數
以下代表將 thread 設定為 5 個
spring:
quartz:
properties:
org:
quartz:
threadPool:
threadCount: 5
persistence
quartz 支援兩種 persistence 方式
memory
預設。每次停止 application 就會把資料丟掉
JDBC
pom.xml 加上 JDBC
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/testweb
username: root
password: password
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: always # always
overwrite-existing-jobs: true
properties:
org:
quartz:
threadPool:
threadCount: 5
initialize-schema 有三種
ALWAYS: 每一次都會重建
EMBEDDED: 只初始化嵌入式 DB
NEVER
動態維護 task
如果 task 太多,會有大量設定的 code,可改用 SchedulerFactoryBean 動態 add/remove task
package com.test.quartz;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@RequiredArgsConstructor
@Configuration
public class TaskConfig {
public static final String SIMPLE_TASK = "simple-task";
private final SchedulerFactoryBean schedulerFactoryBean;
@PostConstruct
public void init() throws SchedulerException {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
boolean exists = scheduler.checkExists(JobKey.jobKey(SIMPLE_TASK));
if (!exists) {
scheduler.scheduleJob(simpleTask(), simpleTaskTrigger());
}
}
// @Bean
public JobDetail simpleTask() {
return JobBuilder.newJob(SimpleTask.class)
.withIdentity(SIMPLE_TASK)
.withDescription("simple-task")
.storeDurably()
.build();
}
// @Bean
public Trigger simpleTaskTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/3 * * * * ? *");
return TriggerBuilder.newTrigger()
.withIdentity("simple-task-trigger")
.forJob(simpleTask())
.withSchedule(cronScheduleBuilder)
.build();
}
}