一、Quartz是什么
导入讲解Quartz的深度好文:Quartz使用总结 作者:路边飞
Quartz是一个任务调度框架。比如你遇到这样的问题。
- 想每月25号,信用卡自动还款
- 想每年4月1日自己给当年暗恋女神发一封匿名贺卡
- 想每隔1小时,备份一下自己的爱情动作片 学习笔记到云盘
这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活。
二、SpringBoot内置定时器
首先导入依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
SpringBoot内置的定时器需要使用@Scheduled注解的cron属性来指明运行周期。
cron属性共有七位,分别是 秒、分、小时、每日、每月、星期、年
0 15 10 ? * * 每天10:15运行
0 * 14 * * ? 每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。
0 0/5 14 * * ? 每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。
0 15 10 15 * ? 每月15日10:15分运行。
@Component
public class SchedulerTask {
@Scheduled(cron = "*/6 * * * * ?")
private void process() {
System.out.println("现在时间:" + dateToDateStr(new Date(), DEFAULT_FORMAT));
}
}
@SpringBootApplication
@EnableScheduling
public class QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzApplication.class, args);
}
}
每隔六秒执行一次 。除了使用cron属性以外,还能换成fixedRate固定的周期。
- @Scheduled(fixedRate = 6000) :上一次开始执行时间点之后 6 秒再执行。
- @Scheduled(fixedDelay = 6000):上一次执行完毕时间点之后 6 秒再执行。
三、Quartz
quartz音括子。
Quartz有四个核心概念:
Job(任务)
定义任务的执行逻辑,在实现接口的 execute 方法中编写所需要定时执行的 Job(任务)
JobDetail(任务信息)
JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。
Trigger(触发器)
触发 Job 执行的时间触发规则,主要有 SimpleTrigger 和 CronTrigger 这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案
Scheduler(调度器)
调度器就相当于一个容器,用于绑定Trigger与JobDetail。两者在 Scheduler 中拥有各自的不同的组及名称。
为什么设计成JobDetail + Job,不直接使用Job?这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
四、SimpleSchedule
使用SimpleTrgger表明定时任务不是很复杂,仅仅运行一次或者按照指定的周期进行运行。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
先编写一个Job接口实现,编写它的任务方法,并表面其 运行时需要提供的数据信息。Job 运行时的信息保存在 JobDataMap 实例中。需要由JobDetail来指明。
@Getter
@Setter
public class SampleJob extends QuartzJobBean {
private String name;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(String.format("Hello %s!", this.name));
}
}
再编写一个JobDetail来描述 Job 的实现类及其他相关的运行时信息。
@Bean
public JobDetail generateJobDetail() {
return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob").usingJobData("name", "World").
storeDurably().build();
}
最后再编写触发规则
@Bean
public Trigger sampleJobTrigger() {
//触发规则:指定每两秒执行一次
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2).repeatForever();
//将触发规则与JobDetail绑定
return TriggerBuilder.newTrigger().forJob(generateJobDetail())
.withIdentity("sampleTrigger").withSchedule(scheduleBuilder).build();
}
最后将这个触发规则Trigger与任务对象JobDetail注册到同一个容器Scheduler中。
@Configuration
public class SampleScheduler {
@Bean
public JobDetail generateJobDetail(){...}
@Bean
public Trigger sampleJobTrigger() {...}
}
五、CronSchedule 复杂调度任务
按照上述编写习惯,首先先编写一个Job接口实现,然后再编写一个JobDetail来包装Job接口实现。再编写一个触发规则Trigger。最后将JobDetail与Trigger绑定到一起并加入Schedule中。
public class ScheduledJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("schedule job1 is running ...");
}
}
@Component
public class MySchedule {
/**
* 生成Schedule的工厂
*/
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
public void startScheduleTask() throws SchedulerException {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduleJob1(scheduler);
}
private void scheduleJob1(Scheduler scheduler) throws SchedulerException {
//先通过Job生成JobDetail
JobDetail jobDetail = JobBuilder.newJob(ScheduledJob.class).
withIdentity("job1", "groupName").build();
//核心:编写cron周期规则
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/6 * * * * ?");
//Trigger
CronTrigger cronTrigger = TriggerBuilder.newTrigger().
withIdentity("trigger1", "group1").withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}
最后在项目启动的时候,开启定时任务
@Component
public class MyStartupRunner implements CommandLineRunner {
@Autowired
public MySchedule scheduleJobs;
@Override
public void run(String... args) throws Exception {
scheduleJobs.startScheduleTask();
System.out.println(">>>>>>>>>>>>>>>定时任务开始执行<<<<<<<<<<<<<");
}
}