一、介绍
- 简单高效。Quartz用起来非常简单。你只要写一个继承org.quartz.Job的job类,将逻辑写入execute()方法,并将这个类设定好时间,之后Quartz就能自动帮你监控这个任务,到了指定时间自动执行,就不再需要你管这些任务了。
- 容错。 如果其中的一个节点挂了,并不会影响其他节点上面任务的执行。
- 支持分布式
二、主要API
- Scheduler - 与调度器交互的主要API。
- Job - 需要被调度器调度的任务必须实现的接口。
- JobDetail - 用于定义任务的实例。
- Trigger - 用于定义调度器何时调度任务执行的组件。
- JobBuilder - 用于定义或创建JobDetail的实例 。
- TriggerBuilder - 用于定义或创建触发器实例。
三、数据库配置
备注:也可不使用数据库,但是使用数据库能实现动态配置。
-- auto-generated definition
create table t_bi_quartz_task_config
(
id int auto_increment comment '配置Id'
primary key,
task_code varchar(32) not null comment '任务编码',
cron varchar(20) not null comment 'cron表达式',
class_name varchar(255) not null comment 'job引用地址',
description varchar(255) null comment '描述',
del_flag tinyint(1) default 0 not null comment '删除状态(0:正常; 1:已删除)',
create_by varchar(32) null comment '创建人',
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
update_by varchar(32) null comment '更新人',
update_time datetime default CURRENT_TIMESTAMP null comment '更新时间'
)
comment '定时任务配置' charset = utf8mb4;
create index idx_task_code
on t_bi_quartz_task_config (task_code);
四、项目整合
1、Mybatis Plus生成的文件
/**
* 定时任务配置
* @TableName t_bi_quartz_task_config
*/
@TableName(value ="t_bi_quartz_task_config")
@Data
public class BiQuartzTaskConfig implements Serializable {
/**
* 配置Id
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 任务编码
*/
@TableField(value = "task_code")
private String taskCode;
/**
* cron表达式
*/
@TableField(value = "cron")
private String cron;
/**
* job引用地址
*/
@TableField(value = "class_name")
private String className;
/**
* 描述
*/
@TableField(value = "description")
private String description;
/**
* 删除状态(0:正常; 1:已删除)
*/
@TableField(value = "del_flag")
private Integer delFlag;
/**
* 创建人
*/
@TableField(value = "create_by")
private String createBy;
/**
* 创建时间
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 更新人
*/
@TableField(value = "update_by")
private String updateBy;
/**
* 更新时间
*/
@TableField(value = "update_time")
private Date updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
/**
* 定时任务配置
* @author He PanFu
* @date 2022-04-12 17:51:11
* @description 针对表【t_bi_quartz_task_config(定时任务配置)】的数据库操作Service
*/
public interface IBiQuartzTaskConfigService extends IService<BiQuartzTaskConfig> {
/**
* 获取所有定时任务配置
* @return 定时任务配置集合
*/
List<BiQuartzTaskConfig> listBiQuartzTaskConfigAll();
/**
* 根据任务编码获取任务配置
*
* @param taskCode 任务编码
* @return 任务配置
*/
BiQuartzTaskConfig getBiQuartzTaskConfigByCode(String taskCode);
}
/**
* 定时任务配置
*
* @author He PanFu
* @date 2022-04-12 17:51:11
* @description 针对表【t_bi_quartz_task_config(定时任务配置)】的数据库操作Service实现
*/
@Service
@RequiredArgsConstructor
public class BiQuartzTaskConfigServiceImpl extends ServiceImpl<BiQuartzTaskConfigMapper, BiQuartzTaskConfig>
implements IBiQuartzTaskConfigService {
private final BiQuartzTaskConfigMapper biQuartzTaskConfigMapper;
@Override
public List<BiQuartzTaskConfig> listBiQuartzTaskConfigAll() {
return biQuartzTaskConfigMapper.listBiQuartzTaskConfigAll();
}
@Override
public BiQuartzTaskConfig getBiQuartzTaskConfigByCode(String taskCode) {
return biQuartzTaskConfigMapper.getBiQuartzTaskConfigByCode(taskCode);
}
}
/**
* 定时任务配置
*
* @author He PanFu
* @date 2022-04-12 17:51:11
* @description 针对表【t_bi_quartz_task_config(定时任务配置)】的数据库操作Mapper
* @entity BiQuartzTaskConfig
*/
public interface BiQuartzTaskConfigMapper extends BaseMapper<BiQuartzTaskConfig> {
/**
* 获取所有定时任务配置
*
* @return 定时任务配置集合
*/
List<BiQuartzTaskConfig> listBiQuartzTaskConfigAll();
/**
* 根据任务编码获取任务配置
*
* @param taskCode 任务编码
* @return 任务配置
*/
BiQuartzTaskConfig getBiQuartzTaskConfigByCode(String taskCode);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.system.modules.bi.mapper.BiQuartzTaskConfigMapper">
<resultMap id="BaseResultMap" type="com.system.modules.bi.entity.BiQuartzTaskConfig">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="taskCode" column="task_code" jdbcType="VARCHAR"/>
<result property="cron" column="cron" jdbcType="VARCHAR"/>
<result property="className" column="class_name" jdbcType="VARCHAR"/>
<result property="description" column="description" jdbcType="VARCHAR"/>
<result property="delFlag" column="del_flag" jdbcType="TINYINT"/>
<result property="createBy" column="create_by" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id
,task_code,cron,
class_name,description,del_flag,
create_by,create_time,update_by,
update_time
</sql>
<select id="listBiQuartzTaskConfigAll"
resultType="com.system.modules.bi.entity.BiQuartzTaskConfig">
select task_code, cron, class_name, description
from t_bi_quartz_task_config
where del_flag = 0
</select>
<select id="getBiQuartzTaskConfigByCode"
resultType="com.system.modules.bi.entity.BiQuartzTaskConfig">
select task_code, cron, class_name, description
from t_bi_quartz_task_config
where task_code = #{taskCode}
and del_flag = 0
</select>
</mapper>
2、导包
<!--quartz和springboot的整合包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
3、自动注入工厂创建
注:只有有这个配置,quartz才可以使用自动注入,不然quartz不会注入到Spring容器中。
/**
* 当配置这个config后,quartz才可以使用autowired注入
*
* @author He PanFu
* @date 2022-03-24 13:48:47
*/
@Component
public class QuartzScheduleAutoBeanFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
/**
*
*
* @author He PanFu
* @date 2022-03-24 13:48:47
*/
@Configuration
public class QuartzScheduleJobConfig {
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
/**
* 配置SchedulerFactoryBean,将一个方法产生为Bean并交给Spring容器管理
* @param jobFactory 任务工厂
* @return SchedulerFactoryBean
*/
@Bean("schedulerFactoryBean")
public SchedulerFactoryBean createFactoryBean(JobFactory jobFactory) {
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setJobFactory(jobFactory);
factoryBean.setTaskExecutor(threadPoolTaskExecutor);
factoryBean.setOverwriteExistingJobs(true);
return factoryBean;
}
/**
* 通过这个类对定时任务进行操作
*
* @param factoryBean 工厂
* @return 定时任务
*/
@Bean
public Scheduler scheduler(@Qualifier("schedulerFactoryBean") SchedulerFactoryBean factoryBean) {
return factoryBean.getScheduler();
}
}
4、Quartz管理 工具类
/**
* Quartz管理 工具类
*
* @author He PanFu
* @date 2022-03-23 16:19:33
*/
@Slf4j
@Component
public class QuartzManagerUtil implements ApplicationContextAware {
private static Scheduler scheduler;
private static IBiQuartzTaskConfigService biQuartzTaskConfigService;
/**
* 程序启动开始加载所有定时任务
*/
public static void initAllJob() {
log.info("程序启动开始加载所有定时任务");
final List<BiQuartzTaskConfig> taskConfigAll = biQuartzTaskConfigService.listBiQuartzTaskConfigAll();
if (CollectionUtils.isEmpty(taskConfigAll)) {
log.info("定时任务加载数据为空");
return;
}
for (BiQuartzTaskConfig taskConfig : taskConfigAll) {
CronTrigger cronTrigger = null;
JobDetail jobDetail = null;
try {
cronTrigger = getCronTrigger(taskConfig);
jobDetail = getJobDetail(taskConfig);
scheduler.scheduleJob(jobDetail, cronTrigger);
log.info("编码:{}定时任务加载成功", taskConfig.getTaskCode());
} catch (Exception e) {
log.error("编码:{}定时任务加载失败", taskConfig.getTaskCode());
}
}
}
/**
* 停止任务
*
* @param taskCode 任务编码
*/
public static void stopJob(String taskCode) throws SchedulerException {
log.info("停止任务:{}", taskCode);
scheduler.pauseJob(JobKey.jobKey(taskCode));
}
/**
* 恢复任务
*
* @param taskCode 任务编码
* @throws SchedulerException 异常
*/
public static void resumeJob(String taskCode) throws SchedulerException {
log.info("恢复任务:{}", taskCode);
scheduler.resumeJob(JobKey.jobKey(taskCode));
}
/**
* 移除任务
*
* @param taskCode 任务编码
*/
public static void removeJob(String taskCode) throws SchedulerException {
log.info("移除任务:{}", taskCode);
// 获取以前的触发器
TriggerKey triggerKey = TriggerKey.triggerKey(taskCode);
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 删除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(taskCode));
}
/**
* 重新加载任务
*
* @param taskCode 任务编码
* @throws Exception 异常
*/
public static void reload(String taskCode) throws Exception {
log.info("重新加载任务:{}", taskCode);
final BiQuartzTaskConfig taskConfig = biQuartzTaskConfigService.getBiQuartzTaskConfigByCode(taskCode);
if (taskConfig == null) {
log.error("未找到相关Job配置,任务编码:{}", taskCode);
throw new SchedulerConfigException("未找到相关Job配置");
}
// 获取以前的触发器
TriggerKey triggerKey = TriggerKey.triggerKey(taskCode);
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 删除触发器
scheduler.unscheduleJob(triggerKey);
// 删除原来的job
scheduler.deleteJob(JobKey.jobKey(taskCode));
JobDetail jobDetail = getJobDetail(taskConfig);
CronTrigger cronTrigger = getCronTrigger(taskConfig);
// 重新加载job
scheduler.scheduleJob(jobDetail, cronTrigger);
}
/**
* 添加新任务
*
* @param taskCode 任务编码
* @throws SchedulerConfigException 异常
*/
public static void addNewJob(String taskCode) throws SchedulerConfigException {
log.info("添加新任务:{}", taskCode);
final BiQuartzTaskConfig taskConfig = biQuartzTaskConfigService.getBiQuartzTaskConfigByCode(taskCode);
if (taskConfig == null) {
log.error("未找到相关Job配置,任务编码:{}", taskCode);
throw new SchedulerConfigException("未找到相关Job配置");
}
try {
JobDetail jobDetail = getJobDetail(taskConfig);
CronTrigger cronTrigger = getCronTrigger(taskConfig);
scheduler.scheduleJob(jobDetail, cronTrigger);
} catch (Exception e) {
log.error("加载定时任务异常", e);
throw new SchedulerConfigException("加载定时任务异常", e);
}
}
/**
* 获取任务是否存在
* <p>
* STATE_BLOCKED 4 阻塞
* STATE_COMPLETE 2 完成
* STATE_ERROR 3 错误
* STATE_NONE -1 不存在
* STATE_NORMAL 0 正常
* STATE_PAUSED 1 暂停
*
* @param taskCode 任务编码
*/
public static Boolean notExists(String taskCode) {
log.info("获取任务是否存在:{}", taskCode);
try {
return scheduler.getTriggerState(TriggerKey.triggerKey(taskCode)) == Trigger.TriggerState.NONE;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 组装JobDetail
*
* @param taskConfig 任务配置
* @return CronTrigger
* @throws ClassNotFoundException 异常
*/
private static JobDetail getJobDetail(BiQuartzTaskConfig taskConfig) throws ClassNotFoundException {
Class<? extends Job> aClass = Class.forName(taskConfig.getClassName()).asSubclass(Job.class);
return JobBuilder.newJob()
.withIdentity(JobKey.jobKey(taskConfig.getTaskCode()))
.withDescription(taskConfig.getDescription())
.ofType(aClass).build();
}
/**
* 组装CronTrigger
*
* @param taskConfig 任务配置
* @return CronTrigger
*/
private static CronTrigger getCronTrigger(BiQuartzTaskConfig taskConfig) {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(taskConfig.getCron());
return TriggerBuilder.newTrigger()
.withIdentity(TriggerKey.triggerKey(taskConfig.getClassName()))
.withSchedule(cronScheduleBuilder)
.build();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
scheduler = (Scheduler) applicationContext.getBean("scheduler");
biQuartzTaskConfigService = applicationContext.getBean(IBiQuartzTaskConfigService.class);
}
}
5、测试
/**
* @author He PanFu
* @date 2021-10-14 14:34:57
*/
@RequestMapping("/test/quartz")
@RestController
public class QuartzController {
@GetMapping("/task/{taskCode}/{type}")
public String task(@PathVariable("taskCode") String taskCode, @PathVariable("type") Integer type) throws Exception {
if (1 == type) {
QuartzManagerUtil.initAllJob();
} else if (2 == type) {
QuartzManagerUtil.stopJob(taskCode);
} else if (3 == type) {
QuartzManagerUtil.resumeJob(taskCode);
} else if (4 == type) {
QuartzManagerUtil.removeJob(taskCode);
} else if (5 == type) {
QuartzManagerUtil.reload(taskCode);
} else if (6 == type) {
QuartzManagerUtil.addNewJob(taskCode);
}
return "task";
}
}