一、Quartz是什么
Quartz是开源调度框架
Quartz 具有以下特点:
1.强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
2.灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
3.分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
quartz调度核心元素:
1.Scheduler:任务调度器,是实际执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。
2.Trigger:触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
3.Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。
4.JobDetail:用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只需要执行某个类的某个方法,就可以通过MethodInvokingJobDetailFactoryBean来调用。
5.Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。实现Job接口的任务,默认是无状态的,若要将Job设置成有状态的,在quartz中是给实现的Job添加@DisallowConcurrentExecution注解(以前是实现StatefulJob接口,现在已被Deprecated),在与spring结合中可以在spring配置文件的job detail中配置concurrent参数。
Quartz是一个强大任务调度框架,有很多的应用场景,在这里只使用Quartz定时发送邮件
二、使用Quartz之前的准备
1.创建springboot项目
2.引入需要的依赖
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-devtools
true
org.springframework.boot
spring-boot-starter-mail
org.quartz-scheduler
quartz-jobs
2.2.1
三.发送邮件的准备
见前面代码
四.编写Quartz任务
1.定义一个工作工厂,JobFactory
package com.javaemaildemo.conf;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
/**
* @author ${lcl}
* @Title: JobFactory
* @ProjectName javaemaildemo
* @Description: TODO
* @date 2019/2/27 002716:06
*/
@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;
}
}
2.创建QuartzConfigration类,开始从定时器工厂中,找到定时器Bean。
package com.javaemaildemo.conf;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
/**
* @author ${lcl}
* @Title: QuartzConfigration
* @ProjectName javaemaildemo
* @Description: TODO
* @date 2019/2/27 002715:23
*/
@Configuration
@EnableScheduling
public class QuartzConfigration {
@Autowired
private JobFactory jobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setJobFactory(jobFactory);
return schedulerFactoryBean;
}
// 创建schedule
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactoryBean().getScheduler();
}
}
3.创建IQuartzService接口,处理Quartz业务逻辑
package com.javaemaildemo.service;
/**
* @author ${lcl}
* @Title: IQuartzService
* @ProjectName javaemaildemo
* @Description: TODO
* @date 2019/2/27 002716:23
*/
public interface IQuartzService{
void startJob(String time, String jobName, String group, Class job);
/****
* 暂停一个任务
* @param triggerName
* @param triggerGroupName
*/
void pauseJob(String triggerName, String triggerGroupName);
/****
* 暂停重启一个定时器任务
* @param triggerName
* @param triggerGroupName
*/
void resumeJob(String triggerName, String triggerGroupName);
/****
* 删除一个定时器任务,删除了,重启就没什么用了
* @param triggerName
* @param triggerGroupName
*/
void deleteJob(String triggerName, String triggerGroupName);
/***
* 根据出发规则匹配任务,立即执行定时任务,暂停的时候可以用
*/
void doJob(String triggerName, String triggerGroupName);
/***
* 开启定时器,这时才可以开始所有的任务,默认是开启的
*/
void startAllJob();
/**
* 关闭定时器,则所有任务不能执行和创建
*/
void shutdown();
}
4.创建QuartzServiceImpl实现接口类
package com.javaemaildemo.service.impl;
import com.javaemaildemo.service.IQuartzService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* @author ${lcl}
* @Title: QuartzServiceImpl
* @ProjectName javaemaildemo
* @Description: TODO
* @date 2019/2/27 002716:08
*/
/**
SimpleScheduleBuilder是简单调用触发器,它只能指定触发的间隔时间和执行次数;
CronScheduleBuilder是类似于Linux Cron的触发器,它通过一个称为CronExpression的规则来指定触发规则,通常是每次触发的具体时间;(关于CronExpression,详见:官方,中文网文)
CalendarIntervalScheduleBuilder是对CronScheduleBuilder的补充,它能指定每隔一段时间触发一次。
*/
@Service
public class QuartzServiceImpl implements IQuartzService {
@Autowired
private Scheduler scheduler;
@Override
public void startJob(String time, String jobName, String group, Class job) {
try {
// 创建jobDetail实例,绑定Job实现类
// 指明job的名称,所在组的名称,以及绑定job类
JobDetail jobDetail = JobBuilder.newJob(job).withIdentity(jobName, group).build();//设置Job的名字和组
//corn表达式 每x秒执行一次
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(time);
//设置定时任务的时间触发规则
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, group).withSchedule(scheduleBuilder).build();
System.out.println(scheduler.getSchedulerName());
// 把作业和触发器注册到任务调度中, 启动调度
scheduler.scheduleJob(jobDetail, cronTrigger);
/*
// 启动调度
scheduler.start();
Thread.sleep(30000);
// 停止调度
scheduler.shutdown();*/
} catch (Exception e) {
e.printStackTrace();
}
}
/****
* 暂停一个任务
* @param triggerName
* @param triggerGroupName
*/
@Override
public void pauseJob(String triggerName, String triggerGroupName) {
try {
JobKey jobKey = new JobKey(triggerName, triggerGroupName);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return;
}
System.out.println("开始暂停一个定时器");
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/****
* 暂停重启一个定时器任务
* @param triggerName
* @param triggerGroupName
*/
@Override
public void resumeJob(String triggerName, String triggerGroupName) {
try {
JobKey jobKey = new JobKey(triggerName, triggerGroupName);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return;
}
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/****
* 删除一个定时器任务,删除了,重启就没什么用了
* @param triggerName
* @param triggerGroupName
*/
@Override
public void deleteJob(String triggerName, String triggerGroupName) {
try {
JobKey jobKey = new JobKey(triggerName, triggerGroupName);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return;
}
scheduler.deleteJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/***
* 根据出发规则匹配任务,立即执行定时任务,暂停的时候可以用
*/
@Override
public void doJob(String triggerName, String triggerGroupName) {
try {
JobKey jobKey = JobKey.jobKey(triggerName, triggerGroupName);
scheduler.triggerJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/***
* 开启定时器,这时才可以开始所有的任务,默认是开启的
*/
@Override
public void startAllJob() {
try {
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 关闭定时器,则所有任务不能执行和创建
*/
@Override
public void shutdown() {
try {
scheduler.shutdown();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
5.创建任务HelloJob继承job类
package com.javaemaildemo.job;
import com.javaemaildemo.service.IEmailService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ResponseBody;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author ${lcl}
* @Title: HelloJob
* @ProjectName javaemaildemo
* @Description: TODO
* @date 2019/2/27 002713:13
*/
public class HelloJob implements Job {
@Autowired
private IEmailService emailService;
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//打印当前的执行时间 例如 2017-11-23 00:00:00
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("现在的时间是:" + sf.format(date));
//具体的业务逻辑
System.out.println("开始任务");
emailService.sendAttachmentMail("[email protected]","定时发送时","下午好");
}
}
6.QuartzConfigration控制执行,注意观察控制台
package com.javaemaildemo.controller;
import com.javaemaildemo.job.HelloJob;
import com.javaemaildemo.service.IQuartzService;
import com.javaemaildemo.service.impl.QuartzServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author ${lcl}
* @Title: QuartzController
* @ProjectName javaemaildemo
* @Description: TODO
* @date 2019/2/27 002716:11
*/
@RestController
public class QuartzController {
@Resource
private IQuartzService quartzService;
@GetMapping("quartzStart")
public String startNNoQuartz() {
quartzService.startJob("0 23 10 * * ? ", "job1", "gropu1", HelloJob.class);
// quartzService.startJob("0/2 * * * * ? ", "job2", "gropu2", HelloJoTwo.class);
return "定时器任务开始执行,请注意观察控制台";
}
@GetMapping("pauseJob")
public String pauseJob() {
quartzService.pauseJob("job1", "gropu1");
return "暂停一个定时器任务,请注意观察控制台";
}
@GetMapping("resumeJob") //shutdown关闭了,或者删除了就不能重启了
public String resumeJob() {
quartzService.resumeJob("job1", "gropu1");
return "暂停重启一个定时器任务,请注意观察控制台";
}
@GetMapping("deleteJob")
public String deleteJob() {
quartzService.deleteJob("job1", "gropu1");
return "删除一个定时器任务,请注意观察控制台,删除了,重启就没什么用了";
}
@GetMapping("doJob")
public String doJob() {
quartzService.doJob("job1", "gropu1");
return "根据出发规则匹配任务,立即执行定时任务,暂停的时候可以用";
}
@GetMapping("startAllJob")
public String startAllJob() {
quartzService.startAllJob();
return "开启定时器,这时才可以开始所有的任务,默认是开启的";
}
@GetMapping("shutdown")
public String shutdown() {
quartzService.shutdown();
return "关闭定时器,则所有任务不能执行和创建";
}
}
表达式 | 允许值 |
---|---|
“0 0 12 * * ?” | 每天中午12点触发 |
“0 15 10 ? * *” | 每天上午10:15触发 |
“0 15 10 * * ? 2005” | 2005年的每天上午10:15触发 |
“0 * 14 * * ?” | 在每天下午2点到下午2:59期间的每1分钟触发 |
“0 0/5 14 * * ?” | 在每天下午2点到下午2:55期间的每5分钟触发 |
“0 0/5 14,18 * * ?” | 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 |
“0 0-5 14 * * ?” | 在每天下午2点到下午2:05期间的每1分钟触发 |
“0 10,44 14 ? 3 WED” | 每年三月的星期三的下午2:10和2:44触发 |
“0 15 10 ? * MON-FRI” | 周一至周五的上午10:15触发 |
“0 15 10 15 * ?” | 每月15日上午10:15触发 |
“0 15 10 L * ?” | 每月最后一日的上午10:15触发 |
“0 15 10 ? * 6L” | 每月的最后一个星期五上午10:15触发 |
“0 15 10 ? * 6L 2002-2005” | 2002年至2005年的每月的最后一个星期五上午10:15触发 |
“0 15 10 ? * 6#3” | 每月的第三个星期五上午10:15触发 |
0 6 * * * | 每天早上6点 |
0 /2 * * | 每两个小时 |
0 23-7/2,8 * * * | 晚上11点到早上8点之间每两个小时,早上八点 |
0 11 4 * 1-3 | 每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点 |
0 4 1 1 * | 1月1日早上4点 |