Quartz实现JAVA定时任务的动态配置
什么是动态配置定时任务?
⾸先说下这次主题,动态配置。没接触过定时任务的同学可以先看下此篇:
定时任务实现⽅式千⼈千种,不过基础的⽆外乎 1、JDK 的Timer类 2、Quartz 3、SpringTask 。⽣产上三种⽅式我都有使⽤过。但是使⽤过程中⽤的最多的便是xml配置的⽅式,这种⽅式最简单,⽆代码侵⼊,也⽐较好理解。
但是却有个致命的缺点,⽐如你要改某个任务的触发时间,亦或是你要新增⼀个任务,暂停⼀个任务。怎么做?
停应⽤!改XML配置!重新启动!
是不是很致命。最近重新研究了下Quartz的配置,实现了不停机添加、暂停、删除、⽴即触发任务的⽅法,在本篇分享出来,其实也不算是完全的研究,在上家公司已经有⼤佬实现了,这次是也是基于⼤佬的理解重新实现下。
国际惯例~先看效果图 ps:⽂末有彩蛋哦~~
管理界⾯:
效果图:实在不知道该跑什么了,每隔⼗秒打⼀段话吧
技术实现
maven依赖
使⽤springboot做框架⽀持
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
数据表
CREATE TABLE `sys_task` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`job_name` varchar(255) DEFAULT NULL COMMENT '任务名',
`description` varchar(255) DEFAULT NULL COMMENT '任务描述',
`cron_expression` varchar(255) DEFAULT NULL COMMENT 'cron表达式',
`bean_class` varchar(255) DEFAULT NULL COMMENT '任务执⾏时调⽤哪个类的⽅法包名+类名',
`job_status` varchar(255) DEFAULT NULL COMMENT '任务状态',
`job_group` varchar(255) DEFAULT NULL COMMENT '任务分组',
`create_user` varchar(64) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_user` varchar(64) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=32DEFAULT CHARSET=utf8;
实现步骤
①启动项⽬,启动task监听
②读取数据库,将开启的任务job和trigger加载到scheduler调度器
③根据任务调度运⾏job类
④每次运⾏利⽤AdaptableJobFactory实例化job类,以便注⼊要运⾏的service
听着是不是很简单,但却还是⼀头雾⽔,且听我慢慢道来~~
代码逻辑
第⼀步:启动项⽬,加载监听
Quartz配置
Springboot的配置⽅法,常规Spring项⽬可以在xml中配置
@Configuration
public class QuartzConfigration {
@Autowired
private JobFactory jobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
try {
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setQuartzProperties(quartzProperties());
schedulerFactoryBean.setJobFactory(jobFactory);
} catch (Exception e) {
e.printStackTrace();
}
return schedulerFactoryBean;
}
// 指定quartz.properties,可在配置⽂件中配置相关属性
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/config/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
Object();
}
// 创建schedule
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
@Component
@Order(value = 1)
public class ScheduleJobInitListener implements CommandLineRunner {
@Autowired
TaskService scheduleJobService;
@Override
public void arg0) throws Exception {
try {
scheduleJobService.initSchedule();
} catch (Exception e) {
e.printStackTrace();
}
}
}
CommandLineRunner类似Spring框架的ApplicationListener。官⽅的解释是:
Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or Order @Order annotation.
接⼝被⽤作将其加⼊spring容器中时执⾏其run⽅法。多个CommandLineRunner可以被同时执⾏在同⼀个spring上下⽂中并且执⾏顺序是以order注解的参数顺序⼀致。第⼆步:读取数据库,加载scheduler调度器
job⽅法
@Override
public void initSchedule() throws SchedulerException {
// 这⾥获取任务信息数据
null官方更新地址List<TaskDO> jobList = taskMapper.list();
for (TaskDO task : jobList) {
if (Code().JobStatus())) {
quartzManager.addJob(task);
}
}
}
添加任务到Quartz调度器
/**
* 添加任务*/
@SuppressWarnings("unchecked")
public void addJob(TaskDO task) {
try {
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
Class<? extends Job> jobClass = (Class<? extends Job>) (Class.BeanClass()).newInstance()
.getClass());
JobDetail jobDetail = wJob(jobClass).JobName(), JobGroup())// 任务名称和组构成任务key
.build();
// 定义调度触发规则
// 使⽤cornTrigger规则
Trigger trigger = wTrigger().JobName(), JobGroup())// 触发器key
.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
.CronExpression())).startNow().build();
// 把作业和触发器注册到任务调度中
scheduler.scheduleJob(jobDetail, trigger);
// 启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Scheduler作为Quartz的核⼼调度器,有将近50多个API接⼝,包括任务的添加,暂停,恢复,删除等⼀系列的API,这⾥仅介绍⼀些常⽤的,想要了解更多可以稍后看下彩蛋部分。
1、start()⽅法:只有调⽤start()⽅法后,Scheduler线程才开始启动触发器trigger,运⾏job
2、pauseJob(JobKey jobKey) :根据指定的JobDetail key暂停job。
3、resumeJob(JobKey jobKey) :根据指定的key恢复⼀个job。
4、deleteJob(JobKey jobKey) :删除⼀个job
5、triggerJob(JobKey jobKey) :触发⼀个JobDetail(现在执⾏)。
6、rescheduleJob(TriggerKey triggerKey, Trigger newTrigger):
⽤给定的键删除触发器,并存储新的触发器,它必须与同⼀个作业相关联(新触发器必须具有指定的作业名和组)-然⽽,新触发器不必具有与旧触发器相同的名称。
第三步:根据任务调度运⾏job类
其实这⼀步是不需要我们编写的,在我们将正确的JobDetail 和 Trigger 表达式加载到任务调度后,调度器会⾃动触发任务的执⾏
第四步:实例化job类,注⼊要运⾏的service
⼯⼚类
@Component
public class JobFactory extends AdaptableJobFactory {
//这个对象Spring会帮我们⾃动注⼊进来,也属于Spring技术范畴.
//为什么需要这个类呢,在我写的这个demo中,⼤家可以将此类删掉,发现程序也可以正确运⾏,可是我为什么还是加上呢。
//⼤家可以看下我们的任务类,⼤家可以看到Job对象的实例化过程是在Quartz中进⾏的,这时候我们将spring的东西注⼊进来,肯定是⾏不通的,所以需要这个类
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调⽤⽗类的⽅法
Object jobInstance = ateJobInstance(bundle);
//进⾏注⼊
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
任务类
@DisallowConcurrentExecution //作业不并发
@Component
public class HelloWorldJob implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("欢迎使⽤yyblog,这是⼀个定时任务 --⼩卖铺的⽼爷爷!"+ DateUtils.fullTime(new Date()));
}
}
好了,⼤功告成,⼀个简单的动态配置的定时任务已经完成。是不是so easy,下⾯我们再来简单实现下其他的⼏种常⽤的api吧。
暂停⼀个job
public void pauseJob(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.JobName(), JobGroup());
scheduler.pauseJob(jobKey);
}
恢复⼀个job
public void resumeJob(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.JobName(), JobGroup());
}
删除⼀个job
public void deleteJob(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.JobName(), JobGroup());
scheduler.deleteJob(jobKey);
}
⽴即触发job
public void runJobNow(TaskDO task) throws SchedulerException {
JobKey jobKey = JobKey.JobName(), JobGroup());
}
更新job表达式
public void updateJobCron(TaskDO task) throws SchedulerException {
TriggerKey triggerKey = JobName(), JobGroup());
CronTrigger trigger = (CronTrigger) Trigger(triggerKey);
CronScheduleBuilder scheduleBuilder = CronExpression());
trigger = TriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
}
彩蛋部分~
好了,正⽂部分基本上就这些,不多废话了,本⽂没有太多的讲解原理,只是简单的应⽤,⽔平不够还望⼤家见谅。
看了上⾯的效果图是不是还是有种看的见摸不着的感觉,⽼爷爷这⾥也做了个线上的实例供⼤家体验~有兴趣的同学可以亲⾃试验⼀下。实例地址:账号/密码:test/123456 菜单:系统设置/计划任务
当然了还有最重要的源码和API⽂档我也不会忘的。
源码地址:
Quartz⽂档地址:
最后的最后,看都看了,码字不易,顺⼿点个赞⽩~^_^
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论