框架篇-定时任务(三) - Quartz
1.定时任务 - Quartz
1.1 前言
我们刚刚使用Spring自带的定时任务,Spring除了自己的定时任务以外,还可以继承其他的定时任务,如: Quartz
1.2 介绍
image-20200428134540388.pngQuartz核心对象如下:
- Job 任务。表示一个工作,为要具体执行的内容。由希望由调度程序执行的组件实现的接口
- JobDetail 任务细节。 表示一个具体可执行的调度程序,Job是这个可执行的调度程序所执行的的内容。JobDetail还包含了这个任务调度的方案和策略
- Trigger 触发器,执行任务的规则,比如每天执行,或者每小时执行。
- Scheduler 任务调度者。代表了一个调度容器。在一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合时,就可以被Scheduler容器所调度了。
1.3 Helloword
1.3.1 创建SpringBoot项目
image-20200428140415152.png image-20200428151222677.png1.3.2 查看Quartz依赖
-
因为
image-20200428144126550.pngQuartz
启动器
1.3.3 修改启动类
在启动类上添加开启任务调度
image-20200428141537527.png1.3.4 撰写自定义任务
package com.task.quartz.task;
import org.springframework.stereotype.Component;
@Component
public class HelloTask {
public static void task() {
System.out.println("=========任务1=========");
}
}
1.3.5 配置Job
通过先前我们只要,一个任务需要实现Job接口,上面的HelloTask
并没有实现Job接口。也就是Quartz并不知道我们的HelloTask 是一个 Job
。因此我们可以借助MethodInvokingJobDetailFactoryBean
来进行配置JobDetail
。
JobDetail
任务细节,里面包含了 任务,以及任务调度的方案和策略。例如是否并发执行等等。
@Bean
public MethodInvokingJobDetailFactoryBean jobDetail(HelloTask task) {
MethodInvokingJobDetailFactoryBean detailFactoryBean = new MethodInvokingJobDetailFactoryBean();
// 设置是否并发运行
detailFactoryBean.setConcurrent(true);
// 注册 Job
detailFactoryBean.setTargetClass(HelloTask.class);
// 设置要执行的方法
detailFactoryBean.setTargetMethod("task");
return detailFactoryBean;
}
1.3.6 配置触发器
-
cron触发器(CronTriggerFactoryBean)
触发器需要与JobDetail组合
@Bean public CronTriggerFactoryBean cronTrigger(MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean) { CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean(); // 注册 JobDetail cronTriggerFactoryBean.setJobDetail(methodInvokingJobDetailFactoryBean.getObject()); // 设置执行周期 cronTriggerFactoryBean.setCronExpression("0/1 * * * * ?"); return cronTriggerFactoryBean; }
-
普通触发器(SimpleTriggerBean)
@Bean public SimpleTriggerFactoryBean simpleTrigger(MethodInvokingJobDetailFactoryBean factoryBean) { SimpleTriggerFactoryBean simpleTriggerFactoryBean = new SimpleTriggerFactoryBean(); simpleTriggerFactoryBean.setJobDetail(factoryBean.getObject()); simpleTriggerFactoryBean.setStartDelay(1000); return simpleTriggerFactoryBean; }
注意:两个触发器,用一个即可。
1.3.7 配置 Scheduler 容器
@Bean
public SchedulerFactoryBean scheduler(Trigger cronTrigger) {
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
// 容器启动时,会更新已经存在的任务
factoryBean.setOverwriteExistingJobs(true);
// 设置延迟启动时间 单位 秒
factoryBean.setStartupDelay(1);
// 注册触发器
factoryBean.setTriggers(cronTrigger);
return factoryBean;
}
1.3.8 启动测试
image-20200428150745359.png这样我们一个Helloword就写完了。但是我们发现有一些缺点:
- 任务里面的方法必须是静态方法
- 同时需要进行大量的配置
因此我们有没有方式去简化呢,当然是有的的。
1.4 简化 Hellworld
为了更加直观测试,我们重新创建一个项目。
1.4.1 创建项目
image-20200428151131872.png1.4.2 修改启动类
image-20200428152503025.png1.4.3 撰写任务类1
package com.quartz.task;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class HelloTask extends QuartzJobBean {
/**
* 在这个方法里面撰写
*/
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("=====任务1 开始======");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=====任务1 结束======");
}
}
1.4.4 撰写任务类2
package com.quartz.task;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class HelloTask2 extends QuartzJobBean {
/**
* 在这个方法里面撰写
*/
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("=====任务2 开始======");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=====任务2 结束======");
}
}
1.4.5 撰写配置类
package com.quartz.task;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TaskConfig {
/**
* 创建 JobDetail 绑定任务1
*
*/
@Bean
public JobDetail uploadTaskDetail() {
return JobBuilder.newJob(HelloTask.class).withIdentity("helloTask").storeDurably().build();
}
/**
* 创建 JobDetail 绑定任务2
*
*/
@Bean
public JobDetail uploadTaskDetail2() {
return JobBuilder.newJob(HelloTask2.class).withIdentity("helloTask2").storeDurably().build();
}
/**
* 创建触发器 1 并且与任务1绑定
* @return
*/
@Bean
public Trigger updateTaskTrigger() {
CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("*/2 * * * * ?");
return TriggerBuilder.newTrigger().forJob(uploadTaskDetail()).withSchedule(builder).build();
}
/**
* 创建触发器 2 并且与任务2绑定
* @return
*/
@Bean
public Trigger updateTaskTrigger2() {
CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("*/2 * * * * ?");
return TriggerBuilder.newTrigger().forJob(uploadTaskDetail2()).withSchedule(builder).build();
}
}
1.4.6 测试
image-20200428155113968.png满足我们的要求。
1.5 线程池执行任务
代码地址:https://github.com/smallCodeWangzh/quartz-demo-task.git
1.5.1创建项目
还是一样,我们创建一个新的项目,并且导入Quartz
依赖。这里就不截图演示了。
1.5.2 添加任务
我们创建两个service。一个是GoodsService
,一个是BrandService
这两个Service的方法彼此执行各自查询功能
package com.quartz.task.service;
import org.springframework.stereotype.Service;
@Service
public class GoodsService {
public void findAllGoods() {
System.out.println("====任务1: 查询 商品 开始=====");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=======任务1: 查询商品结束 ==========");
}
}
创建 BrandService
image-20200428162110199.pngpackage com.quartz.task.service;
import org.springframework.stereotype.Service;
@Service
public class BrandService {
public void findAllBrand() {
System.out.println("====任务2: 查询 品牌 开始=====");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=======任务2: 查询 品牌 结束 ==========");
}
}
1.5.3 编写工具类
由于我们的Service对象由Spring容器管理,所以我们需要编写一个工具类用来获取容器里面的对象
image-20200428162848565.pngpackage com.quartz.task.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class TaskUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
TaskUtil.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
}
1.5.4 增加JobDetail
image-20200428162231656.pngpackage com.quartz.task;
import com.quartz.task.service.BrandService;
import com.quartz.task.service.GoodsService;
import com.quartz.task.util.TaskUtil;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ScheduleJob extends QuartzJobBean {
// 创建线程池
private ExecutorService executorService = Executors.newFixedThreadPool(10);
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
GoodsService goodsService = TaskUtil.getBean(GoodsService.class);
BrandService brandService = TaskUtil.getBean(BrandService.class);
executorService.execute(() -> {
goodsService.findAllGoods();
});
executorService.execute(() ->{
brandService.findAllBrand();
});
}
}
1.5.5 增加配置
package com.quartz.task.config;
import com.quartz.task.ScheduleJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TaskConfig {
/**
* 创建 JobDetail 绑定任务1
*
*/
@Bean
public JobDetail uploadTaskDetail() {
return JobBuilder.newJob(ScheduleJob.class).withIdentity("helloTask").storeDurably().build();
}
/**
* 创建触发器 1 并且与任务1绑定
* @return
*/
@Bean
public Trigger updateTaskTrigger() {
CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("*/10 * * * * ?");
return TriggerBuilder.newTrigger().forJob(uploadTaskDetail()).withSchedule(builder).build();
}
}
image-20200428163819404.png
当然上述做法,在实际项目中不合理,有很多需要优化的地方,这只是提供一个思路。
同时上述只是定时任务入门,如果牵涉到分布式定时任务,或者说项目需求是动态的定时任务,那么就需要视需求而定。