quartz简介(官网搬下来的!!!):
Quartz是功能强大的开源作业调度库,几乎可以集成到任何Java应用程序中-从最小的独立应用程序到最大的电子商务系统。Quartz可用于创建简单或复杂的计划,以执行数以万计,数以万计的工作。任务定义为标准Java组件的作业,它们实际上可以执行您可以对其执行的任何编程操作。Quartz Scheduler包含许多企业级功能,例如对JTA事务和集群的支持。Quartz是免费使用的,并根据Apache 2.0许可获得许可。
quartz官网:http://www.quartz-scheduler.org/
Quartz可以用来解决定时任务调度的问题,那么他和Spring Task以及ScheduledThreadPool有哪些区别,Quartz的优势在哪里?
Spring Task:
1、默认单线程执行
2、同一个任务执行完之后才可以执行下一个该任务(可配置线程池解决)
3、修改定时计划需要重启项目使之生效
4、任务抛出异常会导致后面的任务不继续执行
5、同一任务多次执行使用相同的任务实例
Quartz:
1、默认多线程执行(默认10个线程,可配置)
2、多任务执行之间互不影响,快慢由cpu决定
3、可以不重启服务的情况动态的修改定时任务频率,实时生效
4、某次任务抛出异常不会影响下次任务的执行
5、每次任务执行都会创建一个新的任务实例执行任务
ScheduledThreadPool:
1、可以实现多线程执行任务
2、没有全面的任务调度api,需要开发者结合线程池的方法自行开发定时功能
解决问题:
1、动态的定时任务(修改定时计划后不需要重启项目实时生效)
2、任务抛出异常后不能影响下次任务的正常执行
效果图:
话不多说,直接放码:
数据库表:
CREATE TABLE `sys_job` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`job_group` varchar(256) DEFAULT NULL COMMENT '任务组',
`job_name` varchar(256) DEFAULT NULL COMMENT '任务名称',
`cron_expression` varchar(128) DEFAULT NULL COMMENT 'cron表达式',
`bean_class` varchar(512) DEFAULT NULL COMMENT '任务类的全路径名',
`job_status` varchar(64) DEFAULT NULL COMMENT '任务状态',
`description` varchar(255) DEFAULT NULL COMMENT '描述',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `jobGroup_jobName_unique` (`job_group`,`job_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `sys_job`(`id`, `job_group`, `job_name`, `cron_expression`, `bean_class`, `job_status`, `description`, `create_time`, `modify_time`) VALUES (1, 'test', 'test_one', '*/10 * * * * ?', 'com.lll.quartz.quartz.job.TestOneJob', 'normal', '第一个测试任务', '2020-12-11 00:00:00', '2020-12-11 00:00:00');
INSERT INTO `sys_job`(`id`, `job_group`, `job_name`, `cron_expression`, `bean_class`, `job_status`, `description`, `create_time`, `modify_time`) VALUES (2, 'test', 'test_two', '30 * * * * ?', 'com.lll.quartz.quartz.job.TestTwoJob', 'normal', '第二个测试任务', '2020-12-11 00:00:00', '2020-12-11 00:00:00');
首先说明,笔者的boot项目版本为spring-boot-2-1-12-released;
添加maven依赖,quartz的依赖版本为2.3.2,是目前(2020-12-11)最新版本
<!-- quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
添加相应的配置类
@Configuration
public class QuartzConfig {
@Autowired
private JobFactory jobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
try {
//设置覆盖已存在的任务
schedulerFactoryBean.setOverwriteExistingJobs(true);
//延迟1秒执行
schedulerFactoryBean.setStartupDelay(1);
//将spring管理job自定义工厂交由调度器维护
schedulerFactoryBean.setJobFactory(jobFactory);
} catch (Exception e) {
e.printStackTrace();
}
return schedulerFactoryBean;
}
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
工厂类:
@Component
public class JobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
//进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
基础任务类:
public interface BaseJob extends Job {
@Override
void execute(JobExecutionContext context) throws JobExecutionException;
}
任务1:
@Slf4j
public class TestOneJob implements BaseJob {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("嘤嘤嘤,人家是第一个定时任务!,现在的时间是:{}", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
}
}
任务2:
@Slf4j
public class TestTwoJob implements BaseJob {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("哇哈哈,我是第二个定时任务!,现在的时间是:{}", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
}
}
初始化监听类(作用是项目启动时将之前持久化的的任务初始化)
@Component
public class ScheduleJobInitListener implements CommandLineRunner {
@Autowired
private ISysJobService sysJobService;
@Override
public void run(String... args) throws Exception {
sysJobService.init();
}
}
service代码,处理任务初始化,添加,删除,修改,暂停,恢复
@Slf4j
@Service
@Transactional
public class SysJobServiceImpl implements ISysJobService {
@Autowired
private Scheduler scheduler;
@Resource
private SysJobMapper sysJobMapper;
@PostConstruct
public void startScheduler() {
try {
// 启动调度器
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
@Override
public void init() {
List<SysJob> sysJobs = sysJobMapper.selectList();
try {
for(SysJob sysJob : sysJobs){
// 构建Job信息
Class<?> clazz = Class.forName(sysJob.getBeanClass());
BaseJob baseJob = (BaseJob) clazz.newInstance();
JobDetail jobDetail = JobBuilder.newJob(baseJob.getClass()).withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).build();
// Cron表达式调度构建器(即任务执行的时间)
/**
* withMisfireHandlingInstructionDoNothing
* ——不触发立即执行
* ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
*/
CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();
//根据Cron表达式构建一个Trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).withSchedule(cron).build();
scheduler.scheduleJob(jobDetail, trigger);
if(JobStatusEnum.stop.getStatus().equals(sysJob.getJobStatus())){
scheduler.pauseJob(JobKey.jobKey(sysJob.getBeanClass(), sysJob.getJobGroup()));
}
}
} catch (Exception e) {
log.error("【定时任务】创建失败!", e);
throw new RuntimeException("【定时任务】创建失败!");
}
}
@Override
public List<SysJob> list(Paginator paginator) {
PageHelper.startPage(paginator.getPageNum(), paginator.getPageSize());
List<SysJob> sysJobs = sysJobMapper.selectList();
paginator.setTotal(new PageInfo(sysJobs).getTotal());
return sysJobs;
}
@Override
public void add(SysJob sysJob) {
// 持久化数据
sysJob.setJobStatus(JobStatusEnum.normal.getStatus());
sysJob.setCreateTime(new Date());
sysJobMapper.insert(sysJob);
try {
// 构建Job信息
Class<?> clazz = Class.forName(sysJob.getBeanClass());
BaseJob baseJob = (BaseJob) clazz.newInstance();
JobDetail jobDetail = JobBuilder.newJob(baseJob.getClass()).withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).build();
// Cron表达式调度构建器(即任务执行的时间)
CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();
//根据Cron表达式构建一个Trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).withSchedule(cron).build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
log.error("【定时任务】创建失败!", e);
throw new RuntimeException("【定时任务】创建失败!");
}
}
@Override
public void update(SysJob sysJob) {
// 持久化数据
SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(sysJob.getId());
if(dbSysJob == null){
return;
}
sysJob.setModifyTime(new Date());
sysJobMapper.updateByPrimaryKeySelective(sysJob);
try {
TriggerKey triggerKey = TriggerKey.triggerKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup());
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 根据Cron表达式构建一个Trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
// 如果任务之前是暂停状态,rescheduleJob方法会重启该任务,需要手动去pauseJob
if(JobStatusEnum.stop.getStatus().equals(dbSysJob.getJobStatus())){
scheduler.pauseJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
}
} catch (Exception e) {
log.error("【定时任务】更新失败!", e);
throw new RuntimeException("【定时任务】创建失败!");
}
}
@Override
public void pause(Integer id) {
SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(id);
if(dbSysJob == null){
return;
}
SysJob sysJob = new SysJob();
sysJob.setId(id);
sysJob.setJobStatus(JobStatusEnum.stop.getStatus());
sysJob.setModifyTime(new Date());
sysJobMapper.updateByPrimaryKeySelective(sysJob);
try {
scheduler.pauseJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
} catch (Exception e) {
log.error("【定时任务】暂停失败!", e);
throw new RuntimeException("【定时任务】暂停失败!");
}
}
@Override
public void resume(Integer id) {
SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(id);
if(dbSysJob == null){
return;
}
SysJob sysJob = new SysJob();
sysJob.setId(id);
sysJob.setJobStatus(JobStatusEnum.normal.getStatus());
sysJob.setModifyTime(new Date());
sysJobMapper.updateByPrimaryKeySelective(sysJob);
try {
scheduler.resumeJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
} catch (Exception e) {
log.error("【定时任务】恢复失败!", e);
throw new RuntimeException("【定时任务】恢复失败!");
}
}
@Override
public void delete(Integer id) {
SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(id);
if(dbSysJob == null){
return;
}
sysJobMapper.deleteByPrimaryKey(id);
try {
scheduler.pauseTrigger(TriggerKey.triggerKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
scheduler.unscheduleJob(TriggerKey.triggerKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
scheduler.deleteJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
} catch (Exception e) {
log.error("【定时任务】删除失败!", e);
throw new RuntimeException("【定时任务】删除失败!");
}
}
还有一些dao层的及前端代码就不贴了,全部代码详见:https://github.com/liangliang1006/lll-quartz.git
获取代码后直接修改一下数据库连接,直接启动就好叻
扩展:
其实我这个demo只是展示了quartz的部分功能,他的还可以实现任务流调度,即a任务完成后触发b任务,同时可以支持任务执行到一定时间或次数后停止继续执行;
因为我也是接触此框架不久,有兴趣的可以一起留言讨论