前言
本文主要介绍了SpringBoot架构下动态定时任务的使用,定时任务表达式配置在数据库中,通过反射执行到目标方法。
Quartz
Quartz 是一个开源的作业调度框架,支持分布式定时任务,Quartz定时任务据我了解可分为Trigger(触发器) 、Job(任务)和Scheduler(调度器) ,定时任务的逻辑大体为:创建触发器和任务,并将其加入到调度器中。
Quartz 的核心类有以下三部分:
任务 Job : 需要实现的任务类,实现 execute() 方法,执行后完成任务; 触发器 Trigger : 包括 SimpleTrigger 和 CronTrigger; 调度器 Scheduler : 任务调度器,负责基于 Trigger触发器,来执行 Job任务.
Trigger 有五种触发器:
SimpleTrigger 触发器:需要在特定的日期/时间启动,且以指定的间隔时间(单位毫秒)重复执行 n 次任务,如 :在 9:00 开始,每隔1小时,每隔几分钟,每隔几秒钟执行一次 。没办法指定每隔一个月执行一次(每月的时间间隔不是固定值)。
CalendarIntervalTrigger 触发器:指定从某一个时间开始,以一定的时间间隔(单位有秒,分钟,小时,天,月,年,星期)执行的任务。
DailyTimeIntervalTrigger 触发器:指定每天的某个时间段内,以一定的时间间隔执行任务。并且支持指定星期。如:指定每天 9:00 至 18:00 ,每隔 70 秒执行一次,并且只要周一至周五执行。
CronTrigger 触发器:基于日历的任务调度器,即指定星期、日期的某时间执行任务。
NthIncludedDayTrigger 触发器:不同时间间隔的第 n 天执行任务。比如,在每个月的第 15 日处理财务发票记帐,同样设定双休日或者假期。
创建任务表
create table sys_job (
job_id bigint ( 20 ) not null auto_increment comment '任务ID' ,
job_name varchar ( 64 ) default '' comment '任务名称' ,
job_group varchar ( 64 ) default 'DEFAULT' comment '任务组名' ,
invoke_target varchar ( 500 ) not null comment '调用目标方法' ,
cron_expression varchar ( 255 ) default '' comment 'cron执行表达式' ,
misfire_policy varchar ( 20 ) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)' ,
concurrent char ( 1 ) default '1' comment '是否并发执行(0允许 1禁止)' ,
status char ( 1 ) default '0' comment '状态(0正常 1暂停)' ,
create_by varchar ( 64 ) default '' comment '创建者' ,
create_time datetime comment '创建时间' ,
update_by varchar ( 64 ) default '' comment '更新者' ,
update_time datetime comment '更新时间' ,
remark varchar ( 500 ) default '' comment '备注信息' ,
primary key ( job_id, job_name, job_group)
) engine = innodb auto_increment = 100 comment = '定时任务调度表' ;
INSERT INTO ` sys_job` ( ` job_id` , ` job_name` , ` job_group` , ` invoke_target` , ` cron_expression` , ` misfire_policy` , ` concurrent` , ` status` , ` create_by` , ` create_time` , ` update_by` , ` update_time` , ` remark` ) VALUES ( 2 , '系统默认(有参)' , 'DEFAULT' , 'com.demo.task.Task.testParams(\'hello\')' , '0/15 * * * * ?' , '3' , '1' , '0' , 'admin' , '2024-01-16 19:07:33' , '' , NULL , '' ) ;
INSERT INTO ` sys_job` ( ` job_id` , ` job_name` , ` job_group` , ` invoke_target` , ` cron_expression` , ` misfire_policy` , ` concurrent` , ` status` , ` create_by` , ` create_time` , ` update_by` , ` update_time` , ` remark` ) VALUES ( 3 , '系统默认(无参)' , 'DEFAULT' , 'task.testNoParams()' , '0/20 * * * * ?' , '3' , '1' , '0' , 'admin' , '2024-01-16 19:07:33' , '' , NULL , '' ) ;
create table sys_job_log (
job_log_id bigint ( 20 ) not null auto_increment comment '任务日志ID' ,
job_name varchar ( 64 ) not null comment '任务名称' ,
job_group varchar ( 64 ) not null comment '任务组名' ,
invoke_target varchar ( 500 ) not null comment '调用目标字符串' ,
job_message varchar ( 500 ) comment '日志信息' ,
status char ( 1 ) default '0' comment '执行状态(0正常 1失败)' ,
exception_info varchar ( 2000 ) default '' comment '异常信息' ,
create_time datetime comment '创建时间' ,
primary key ( job_log_id)
) engine = innodb comment = '定时任务调度日志表' ;
添加依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.14</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
定义Job
Quartz定时任务默认都是并发执行的,不会等待上一次任务执行完毕,只要间隔时间到就会执行, 如果定时任执行太长,会长时间占用资源,导致其它任务堵塞。
一般设置都是禁止并发执行
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
@Override
protected void doExecute ( JobExecutionContext context, SysJob sysJob) throws Exception {
JobInvokeUtil . invokeMethod ( sysJob) ;
}
}
public abstract class AbstractQuartzJob implements Job {
private static final Logger log = LoggerFactory . getLogger ( AbstractQuartzJob . class ) ;
private static ThreadLocal < Date > threadLocal = new ThreadLocal < > ( ) ;
@Override
public void execute ( JobExecutionContext context) throws JobExecutionException {
SysJob sysJob = new SysJob ( ) ;
BeanUtils . copyProperties ( context. getMergedJobDataMap ( ) . get ( ScheduleConstants . TASK_PROPERTIES ) , sysJob) ;
try {
before ( context, sysJob) ;
if ( sysJob != null )
{
doExecute ( context, sysJob) ;
}
after ( context, sysJob, null ) ;
}
catch ( Exception e)
{
log. error ( "任务执行异常 - :" , e) ;
after ( context, sysJob, e) ;
}
}
protected void before ( JobExecutionContext context, SysJob sysJob) {
threadLocal. set ( new Date ( ) ) ;
}
protected void after ( JobExecutionContext context, SysJob sysJob, Exception e) {
Date startTime = threadLocal. get ( ) ;
threadLocal. remove ( ) ;
}
protected abstract void doExecute ( JobExecutionContext context, SysJob sysJob) throws Exception ;
}
实体类
@Data
public class SysJob implements Serializable {
private static final long serialVersionUID = 1L ;
private Long jobId;
private String jobName;
private String jobGroup;
private String invokeTarget;
private String cronExpression;
private String misfirePolicy = ScheduleConstants . MISFIRE_DEFAULT ;
private String concurrent;
private String status;
}
创建定时任务
public static void createScheduleJob ( Scheduler scheduler, SysJob job) throws SchedulerException , TaskException {
Class < ? extends Job > jobClass = getQuartzJobClass ( job) ;
Long jobId = job. getJobId ( ) ;
String jobGroup = job. getJobGroup ( ) ;
JobDetail jobDetail = JobBuilder . newJob ( jobClass) . withIdentity ( getJobKey ( jobId, jobGroup) ) . build ( ) ;
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder . cronSchedule ( job. getCronExpression ( ) ) ;
cronScheduleBuilder = handleCronScheduleMisfirePolicy ( job, cronScheduleBuilder) ;
CronTrigger trigger = TriggerBuilder . newTrigger ( ) . withIdentity ( getTriggerKey ( jobId, jobGroup) )
. withSchedule ( cronScheduleBuilder) . build ( ) ;
jobDetail. getJobDataMap ( ) . put ( ScheduleConstants . TASK_PROPERTIES , job) ;
if ( scheduler. checkExists ( getJobKey ( jobId, jobGroup) ) ) {
scheduler. deleteJob ( getJobKey ( jobId, jobGroup) ) ;
}
if ( StringUtils . isNotNull ( CronUtils . getNextExecution ( job. getCronExpression ( ) ) ) ) {
scheduler. scheduleJob ( jobDetail, trigger) ;
}
if ( job. getStatus ( ) . equals ( ScheduleConstants. Status . PAUSE . getValue ( ) ) ) {
scheduler. pauseJob ( ScheduleUtils . getJobKey ( jobId, jobGroup) ) ;
}
}
private static Class < ? extends Job > getQuartzJobClass ( SysJob sysJob) {
boolean isConcurrent = "0" . equals ( sysJob. getConcurrent ( ) ) ;
return isConcurrent ? QuartzJobExecution . class : QuartzDisallowConcurrentExecution . class ;
}
反射类
public class JobInvokeUtil {
public static void invokeMethod ( SysJob sysJob) throws Exception {
String invokeTarget = sysJob. getInvokeTarget ( ) ;
String beanName = getBeanName ( invokeTarget) ;
String methodName = getMethodName ( invokeTarget) ;
List < Object [ ] > methodParams = getMethodParams ( invokeTarget) ;
if ( ! isValidClassName ( beanName) ) {
Object bean = SpringUtils . getBean ( beanName) ;
invokeMethod ( bean, methodName, methodParams) ;
}
else {
Object bean = Class . forName ( beanName) . getDeclaredConstructor ( ) . newInstance ( ) ;
invokeMethod ( bean, methodName, methodParams) ;
}
}
private static void invokeMethod ( Object bean, String methodName, List < Object [ ] > methodParams)
throws NoSuchMethodException , SecurityException , IllegalAccessException , IllegalArgumentException ,
InvocationTargetException {
if ( StringUtils . isNotNull ( methodParams) && methodParams. size ( ) > 0 ) {
Method method = bean. getClass ( ) . getMethod ( methodName, getMethodParamsType ( methodParams) ) ;
method. invoke ( bean, getMethodParamsValue ( methodParams) ) ;
}
else {
Method method = bean. getClass ( ) . getMethod ( methodName) ;
method. invoke ( bean) ;
}
}
}
定时任务类
@Component ( "task" )
@Slf4j
public class Task {
public void testParams ( String params) {
log. info ( "执行有参方法:" + params) ;
System . out. println ( ) ;
}
public void testNoParams ( ) {
log. info ( "执行无参方法" ) ;
}
}
初始化定时任务
@PostConstruct
public void init ( ) throws SchedulerException , TaskException {
scheduler. clear ( ) ;
List < SysJob > jobList = jobMapper. selectList ( null ) ;
for ( SysJob job : jobList) {
ScheduleUtils . createScheduleJob ( scheduler, job) ;
}
}
运行效果:
2024-03-25 14:05:30.020 INFO 11296 — [eduler_Worker-1] com.demo.task.Task : 执行有参方法:hello
2024-03-25 14:05:40.005 INFO 11296 — [eduler_Worker-2] com.demo.task.Task : 执行无参方法 2024-03-25 14:05:45.008 INFO 11296 — [eduler_Worker-3] com.demo.task.Task : 执行有参方法:hello
2024-03-25 14:06:00.012 INFO 11296 — [eduler_Worker-4] com.demo.task.Task : 执行有参方法:hello
2024-03-25 14:06:00.014 INFO 11296 — [eduler_Worker-5] com.demo.task.Task : 执行无参方法
添加定时任务
public int insertJob ( SysJob job) throws SchedulerException , TaskException {
job. setStatus ( ScheduleConstants. Status . PAUSE . getValue ( ) ) ;
int rows = jobMapper. insert ( job) ;
if ( rows > 0 ) {
ScheduleUtils . createScheduleJob ( scheduler, job) ;
}
return rows;
}
解决 Quartz Job 中无法注入 Spring Bean
首先自定义一个 JobFactory,通过 AutowireCapableBeanFactory
将创建好的 Job 对象交给 Spring 管理
@Configuration
public class CustomJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
protected Object createJobInstance ( TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super . createJobInstance ( bundle) ;
autowireCapableBeanFactory. autowireBean ( jobInstance) ;
return jobInstance;
}
}
再创建一个配置类,将自定义的 JobFactory 设置到 Schedule
中
@Configuration
public class QuartzConfig {
@Autowired
private CustomJobFactory customJobFactory;
@SneakyThrows
@Bean
public Scheduler scheduler ( ) {
原文地址:https://blog.csdn.net/qq_30823993/article/details/137016461
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:https://www.msipo.com/article-662622.html 如若内容造成侵权/违法违规/事实不符,请联系MSIPO邮箱:3448751423@qq.com进行投诉反馈,一经查实,立即删除!