????????springboot2集成quartz详细步骤,如何通过接口管理quartz定时任务、如何通过配置实现一个固定的quartz定时任务、如何配置quartz、以及如何自动初始化quartz的数据库。还有讲解如何使用springboot自带的定时器实现定时任务的方式。本文不讲解原理,只讲解如何使用。
1.使用springboot自带的schedule实现定时任务
????????不用引用任何第三方的工具包
a. 首先在启动类上增加@EnableScheduling注解,开启定时任务的支持
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class SpringTestApplication {
public static void main(String[] args){
SpringApplication.run(SpringTestApplication.class, args);
}
}
b. 然后定义自己的定时任务业务逻辑类
????????加上注解@Component或@Configuration,在定时任务的具体逻辑方法加上注解@Schedule("${cron表达式--如:0/10 * * * * ?}")
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class SpringScheduleTest {
/**
* 添加定时任务(使用corn表达式设置定时任务执行时间,例如 10秒:0/10 * * * * ?)
* 可直接指定时间间隔,例如:5秒 @Scheduled(fixedRate=5000)
* @author liuyong
* @date 2022/5/24
*/
@Scheduled(cron = "0/10 * * * * ?")
private void cleanSessionInfo() {
System.err.println("Springboot Schedule定时任务测试");
}
}
这样一定固定的定时任务就完成了,在方法中实现自己的定时任务逻辑即可
2. 集成使用quartz实现定时任务
a. 首先增加pom.xml配置,引入quartz依赖:
<!-- quartz定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
b. 然后在yml文件配置quartz的基础配置:
quartz在spring配置下 spring.quartz
# quartz定时任务配置
quartz:
# 数据库存储方式
job-store-type: jdbc
# 关闭系统的时候,等待定时任务完成
wait-for-jobs-to-complete-on-shutdown: true
# 初始化后是否自动启动计划程序
auto-startup: true
# 延迟启动
startup-delay: 5s
jdbc:
# SQL 初始化脚本中单行注释的前缀
comment-prefix: --
# 数据库架构初始化模式--never 从不进行初始化,always 每次都清空数据库进行初始化,embedded 只初始化内存数据库(默认值)
initialize-schema: never
# 用于初始化数据库架构的SQL文件的路径
schema: classpath:database/mysql.sql
properties:
org:
quartz:
# dataSource:
# quartzDS:
# driver: com.mysql.jdbc.Driver
# url: jdbc:mysql://localhost:3307/quartz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
# username: quartz
# password: quartz
scheduler:
#调度标识名 集群中每一个实例都必须使用相同的名称;ID设置为自动获取 每一个必须不同
instanceName: TestScheduler
# 集群配置节点实例ID
instanceId: AUTO
rmi:
# 如果希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true
# 在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'
# 指定一个'true'值是没有意义的,如果这样做'export'选项将被忽略
export: false
#如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。
#还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
proxy: false
# quartz线程池配置
threadPool:
#实例化ThreadPool时,使用的线程类为SimpleThreadPool
class: org.quartz.simpl.SimpleThreadPool
#threadCount和threadPriority将以setter的形式注入ThreadPool实例
#并发个数:如果只有几个工作每天触发几次 那么1个线程就可以,如果有成千上万的工作,每分钟都有很多工作 那么就需要50-100之间.
#只有1到100之间的数字是非常实用的
threadCount: 10
#优先级 默认值为5
threadPriority: 5
#可以是“true”或“false”,默认为false
threadsInheritContextClassLoaderOfInitializingThread: true
# quartz的任务存储方式
jobStore:
# 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失;
#此处配置通过事务存储数据库;2.5.6版本之后,配置JobStoreTX要报错,调整配置
class: org.quartz.impl.jdbcjobstore.JobStoreTX
# class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
#您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作
# StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#表前缀
tablePrefix: qrtz_
# #数据源别名,自定义
# dataSource: quartzDS
#是否加入集群(集群时,一个任务只有一个服务实例执行)
#设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏
isClustered: flase
#调度实例失效的检查时间间隔
clusterCheckinInterval: 10000
#可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,
#因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题
useProperties: false
c. 自动初始化数据库:
数据库连接,使用正常的springboot项目的jdbc+alibaba的druid即可
首先到地址找到自己使用的数据库的sql文件,并放到自己项目的resource目录下:quartzjdbc/src/main/resources/sql at master · wangmaoxiong/quartzjdbc · GitHubquartz 定时器 jdbc 持久化调度信息. Contribute to wangmaoxiong/quartzjdbc development by creating an account on GitHub.https://github.com/wangmaoxiong/quartzjdbc/tree/master/src/main/resources/sql
然后调整yml中 配置jdbc: initialize-schema: never 与 jdbc: schema: classpath:database/mysql.sql。never该改为always,schema改为自己下载的sql文件存储位置
注意:第一次启动后,需要将always改为never,否则后续每次启动都会重新初始化quartz数据库
d. 定义定时任务业务逻辑类
????????其中将DisallowConcurrentExecution注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例,即同一个job,即使时间到了,只要上一个还在运行,新的就等待
@DisallowConcurrentExecution
public class QuartzTestTask implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info(LocalDateTime.now() + "=======开始我的测试quartz定时任务!");
try {
log.info(LocalDateTime.now() + "=======执行我的测试quartz定时任务中......");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
log.info(LocalDateTime.now() + "=======我的测试quartz定时任务结束!");
}
}
e. 注册定时任务:
? ? ? ? 此处有两种方式:
? ? ? ? ①. 配置固定定时任务
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FixedJobConfig {
// 使用jobDetail包装job
@Bean
public JobDetail myJobDetail() {
return JobBuilder.newJob(QuartzTestTask.class)
.withIdentity("myFixedJob") // 设置job任务的名称
// .usingJobData("userName", "zhangsan")//参数
.storeDurably()
.build();
}
// 把jobDetail注册到trigger上去
@Bean
public Trigger myJobTrigger() {
// 每5秒一次
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(myJobDetail()) //关联jobDetail
.withIdentity("myFixedJobTrigger") // 设置触发器的名称
.withSchedule(cronScheduleBuilder)
.build();
}
}
这样配置后,定时任务即可生效。
? ? ? ? ②. 通过接口管理定时任务:
? ? ? ? 先定义一个quartz定时任务的管理工具类
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* quartz管理工具类
* @author kevin
* @date 2022/5/24
*/
@Slf4j
@Component
public class QuartzManager {
//调度工厂
@Autowired
private SchedulerFactoryBean schedulerFactory;
public Scheduler getScheduled() throws SchedulerException {
return schedulerFactory.getScheduler();
}
/**
* 添加定时任务
* @param jobGroupName job任务分组名称
* @param triggerGroupName job触发器分组名称
* @param jobName 任务名称
* @param jobClazz 任务job
* @param cron 执行时间之cron表达式
* @param params 任务job参数
*/
public void addJob(String jobGroupName, String triggerGroupName, String jobName , Class jobClazz ,
String cron, Map<String,String> params) throws SchedulerException {
//获取Scheduler
Scheduler scheduler = getScheduled();
// 任务名,任务组,任务执行类
JobDetail jobDetail= JobBuilder.newJob(jobClazz)
.withIdentity(jobName, jobGroupName)
.storeDurably() //配置存储到数据库
.build();
// 传参(可选)
JobDataMap jobDataMap = jobDetail.getJobDataMap();
if(params != null ){
params.forEach(jobDataMap::put);
}
//触发器时间设置
CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cron);
// 触发器创建,创建Trigger对象
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName, triggerGroupName)// 触发器名(设置为任务名),触发器组名称
.withSchedule(cronBuilder) // 触发器时间设定
.startNow().build();
// 调度容器设置JobDetail和Trigger
scheduler.scheduleJob(jobDetail, trigger);
// 启动定时任务
if (!scheduler.isShutdown()) {
scheduler.start();
}
}
/**
* 更新定时任务的cron表达式--根据定时任务名称
* @author kevin
* @param triggerGroupName 需要修改的定时任务的触发器分组名称
* @param jobName 需要修改的定时任务的名称
* @param cron 新的cron表达式
* @date 2022/5/24 17:13
*/
public void updateJob(String triggerGroupName, String jobName, String cron) throws SchedulerException{
//获取Scheduler
Scheduler scheduler = getScheduled();
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, triggerGroupName);
// 触发器时间定义
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// 创建Trigger对象
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(scheduleBuilder)
.build();
scheduler.rescheduleJob(triggerKey, cronTrigger);
}
/**
* 删除定时任务--根据定时任务名称
* @param jobGroupName job任务分组名称
* @param triggerGroupName job触发器分组名称
* @param jobName 任务名称
*/
public void removeJob(String jobGroupName, String triggerGroupName, String jobName) throws SchedulerException{
Scheduler scheduler = getScheduled();
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, triggerGroupName);
scheduler.pauseTrigger(triggerKey);// 停止触发器
scheduler.unscheduleJob(triggerKey);// 移除触发器
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
}
/**
* 获取所有定时任务
* @author kevin
* @return java.util.List<java.lang.String>
* @date 2022/5/24 17:06
*/
public List<String> getAllJob() throws SchedulerException{
List<String> jobs = new ArrayList<>();
Set<JobKey> jobKeys = getScheduled().getJobKeys(GroupMatcher.anyGroup());
for(JobKey jobKey:jobKeys){
jobs.add(jobKey.getName());
}
return jobs;
}
/**
* 停止所有的定时任务
* @author kevin
* @date 2022/5/24 17:05
*/
public void shutdownJobs() throws SchedulerException{
Scheduler scheduler = getScheduled();
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
}
/**
* 启动所有的定时任务
* @author kevin
* @date 2022/5/24 17:05
*/
public void startJobs() throws SchedulerException{
Scheduler scheduler = getScheduled();
scheduler.start();
}
}
然后编写接口controller:
import com.liu.utils.QuartzManager;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/job")
public class JobManageController {
@Autowired
private QuartzManager quartzManager;
/**
* 删除job
* @author kevin
* @return java.util.Map<java.lang.String,java.lang.Object>
* @date 2022/5/24 17:57
*/
@GetMapping("/getJobs")
public Map<String, Object> getJobs() {
Map<String, Object> result = new HashMap<>();
try {
result.put("code", 200);
result.put("msg", "定时任务列表查询成功!");
result.put("data", quartzManager.getAllJob());
return result;
} catch (SchedulerException e) {
e.printStackTrace();
result.put("code", 201);
result.put("msg", "定时任务列表查询失败!");
result.put("data", null);
return result;
}
}
/**
* 新增job任务
*
* @param jobName job名称
* @param jobGroupName job分组名称
* @param triggerGroupName 触发器分组名称
* @param className 需要执行的job的类名
* @param cron cron 表达式
* @return java.util.Map<java.lang.String,java.lang.Object>
*/
@PostMapping("/addJob")
public Map<String, Object> addJob(String jobName, String jobGroupName, String triggerGroupName,
String className, String cron, Map<String, String> params) {
Class<? extends Job> jobClass;
try{
jobClass = (Class<? extends Job>) Class.forName("com.liu.task." + className);
quartzManager.addJob(jobGroupName, triggerGroupName, jobName, jobClass, cron, params);
}catch (ClassNotFoundException e){
Map<String, Object> result = new HashMap<>();
result.put("code", 201);
result.put("msg", "定时任务不存在!");
result.put("data", null);
return result;
} catch (SchedulerException e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 201);
result.put("msg", "定时任务创建失败!");
result.put("data", null);
return result;
}
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "定时任务创建成功!");
result.put("data", null);
return result;
}
/**
* 修改定时任务
* @param triggerGroupName 需要修改的定时任务的触发器分组名称
* @param jobName 需要修改的定时任务的名称
* @param cron 新的cron表达式
* @return java.util.Map<java.lang.String,java.lang.Object>
*/
@PutMapping("/updateJob")
public Map<String, Object> updateJob(String triggerGroupName, String jobName, String cron) {
try {
quartzManager.updateJob(triggerGroupName, jobName, cron);
} catch (SchedulerException e) {
e.printStackTrace();
Map<String, Object> result = new HashMap<>();
result.put("code", 201);
result.put("msg", "定时任务更新失败!");
result.put("data", null);
return result;
}
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "定时任务更新成功!");
result.put("data", null);
return result;
}
/**
* 删除job
* @param jobGroupName 定时任务分组名称
* @param triggerGroupName 触发器分组名称
* @param jobName 定时任务名称
* @return java.util.Map<java.lang.String,java.lang.Object>
*/
@DeleteMapping("/deleteJob")
public Map<String, Object> deleteJob(String jobGroupName, String triggerGroupName, String jobName) {
try {
quartzManager.removeJob(jobGroupName, triggerGroupName, jobName);
} catch (SchedulerException e) {
e.printStackTrace();
Map<String, Object> result = new HashMap<>();
result.put("code", 201);
result.put("msg", "定时任务更新失败!");
result.put("data", null);
return result;
}
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "定时任务删除成功!");
result.put("data", null);
return result;
}
}
如此即可通过接口对定时任务进行管理(具体的定时任务逻辑实现类还是需要后台自己编码实现,接口管理只是对已有实现逻辑的定时任务进行配置与管理,其中addJob中className就是自己编码实现的定时任务逻辑实现类的类名)
|