Quartz:基本用法总结
OpenSymphony所提供的Quartz是任务调度领域享誉盛名的开源框架。Spring提供了集成Quartz的功能,可以让开发人员以更面向Spring的方式创建基于Quartz的任务调度应用。任务调度本身设计多线程并发、运行时间规则制定及解析、运行现场保持与恢复、线程池维护等诸多方面的工作。如果以自定义线程池的原始方法开发,难点很大。
1.普通JAVA任务
启动基本的Quartz任务包含一下流程:
- 创建任务类:实现Job接口的void execute(JobExecutionContext context)方法,定义被执行任务的执行逻辑;
- 生成JobDetail对象:通过加载任务类(不是实例)来绑定任务逻辑与任务信息;
- 生成Trigger对象:定时器的触发时间有两种方式可以定义,分别是CronSchedule和simpleSchedule()。前者使用正则表达式,后者则是简单封装后的定时器。
- 获取Scheduler对象:通过
StdSchedulerFactory
工厂方法初始化scheduler对象,把任务和定时器绑定在一起,并启动任务。
完整实例代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
public class RAMQuartz {
private static Logger logger = LoggerFactory.getLogger(RAMQuartz.class);
public static void main(String[] args) throws SchedulerException {
//创建scheduler
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
//定义一个JobDetail
//定义Job类为RAMJob类,这是真正的执行逻辑所在
JobDetail jb = JobBuilder.newJob(RAMJob1.class)
.withDescription("this is a ram job")
.withIdentity("ramJob", "ramGroup")//定义name/group
.build();
//通过JobDataMap传递参数
jb.getJobDataMap().put("Test", "This is test parameter value");
long time = System.currentTimeMillis() + 3*1000L;
Date startTime = new Date(time);
//定义一个Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withDescription("")
.withIdentity("ramTrigger", "ramTriggerGroup")//定义name/group
.startAt(startTime)//加入scheduler后,在指定时间启动
//使用CronTrigger
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
.build();
//绑定任务和定时器到调度器
scheduler.scheduleJob(jb,trigger);
//启动
scheduler.start();
logger.info("启动时间 : " + new Date());
}
}
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
public class RAMJob1 implements Job{
private static Logger logger = LoggerFactory.getLogger(RAMJob.class);
@Override
public void execute(JobExecutionContext jobExecutionContext)
throws JobExecutionException {
try {
JobDataMap dataMap = jobExecutionContext.getJobDetail().getJobDataMap();
String str = dataMap.getString("Test");
logger.info("Quartz dataMap : " + new Date() + "\n" + str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.对象注入
在Spring的WEB应用中使用定时器,通常都会用到spring的特性——对象注入。前面的代码虽然能够很好地执行简单的定时器任务,但是遇到复杂的执行逻辑(如数据库读写等),就不能应付了。
下面代码可以看出,任务2需要执行myBatis的数据库插入语句:
public class RAMJob2 implements Job{
@Autowired
private TestQuartzMapper testQuartzMapper;
private static Logger logger = LoggerFactory.getLogger(RAMJob.class);
@Override
public void execute(JobExecutionContext jobExecutionContext)
throws JobExecutionException {
try {
testQuartzMapper.insertSelective(testQuartz);
logger.info("Insert MyBatis Success!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行这个业务逻辑,就不得不注入对象。如果仍然延用上面的方法,我们会发现执行的时候,testQuartzMapper的对象为null,结果自然毫无悬念地不断报错。
如何为我们的定时器注入Spring的对象,下面介绍一下思路:
- 自定义
JobFactory
工厂方法,扩展AdaptableJobFactory
,重写其createJobInstance
方法; - 声明
SchedulerFactoryBean
,传入自定义的JobFactory
工厂方法; - 通过新的
SchedulerFactoryBean
获取scheduler
实例,用注入的方式在需要的地方使用。
完整示例
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;
@Component
public class MyJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
@Configuration
public class QuartzConfig {
@Autowired
private MyJobFactory myJobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
// 加载quartz数据源配置
factory.setQuartzProperties(quartzProperties());
// 自定义Job Factory,用于Spring注入
factory.setJobFactory(myJobFactory);
return factory;
}
@Bean
public Scheduler scheduler() throws IOException, SchedulerException {
Scheduler scheduler = schedulerFactoryBean().getScheduler();
scheduler.start();
return scheduler;
}
}
3.Spring简单任务
Spring对Quartz进行了封装,方便开发者调用。下面以Spring Boot为例,介绍一下简单任务在Spring的执行方式。
任务类定义
仔细观察可以发现,与普通Java任务的区别在于使用了@Component和@EnableScheduling的注释,相应的,就不用声明implements Job
,以及重写execute
方法。这是Spring提供的一种便利。
@Component
@EnableScheduling
public class SpringJob {
@Autowired
WriteService writeService;
private Logger logger = LoggerFactory.getLogger(this.getClass());
public void myJobBusinessMethod() {
this.logger.info("MyFirstExerciseJob哇被触发了哈哈哈哈哈");
writeService.writeMSG("张三");
}
}
配置JobDetail和Trigger的Bean
MethodInvokingJobDetailFactoryBean
是Spring提供的JobDetail工厂方法,使用它可以快速地定义JobDetail。然而,缺点是生成的任务无法持久化保存,也就是说,无法管理任务的启动、暂停、恢复、停止等操作。
CronTriggerFactoryBean
为表达式型触发器。
@Configuration
public class QuartzJobConfig {
/**
* 方法调用任务明细工厂Bean
*/
@Bean(name = "SpringJobBean")
public MethodInvokingJobDetailFactoryBean myFirstExerciseJobBean(SpringJob springJob) {
MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
jobDetail.setConcurrent(false); // 是否并发
jobDetail.setName("general-springJob"); // 任务的名字
jobDetail.setGroup("general"); // 任务的分组
jobDetail.setTargetObject(springJob); // 被执行的对象
jobDetail.setTargetMethod("myJobBusinessMethod"); // 被执行的方法
return jobDetail;
}
/**
* 表达式触发器工厂Bean
*/
@Bean(name = "SpringJobTrigger")
public CronTriggerFactoryBean myFirstExerciseJobTrigger(@Qualifier("SpringJobBean") MethodInvokingJobDetailFactoryBean springJobBean) {
CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
tigger.setJobDetail(springJobBean.getObject());
tigger.setCronExpression("0/10 * * * * ?"); // 什么是否触发,Spring Scheduler Cron表达式
tigger.setName("general-springJobTrigger");
return tigger;
}
}
调度器
下面将任务和触发器注册到调度器
@Configuration
public class QuartzConfig {
/**
* 调度器工厂Bean
*/
@Bean(name = "schedulerFactory")
public SchedulerFactoryBean schedulerFactory(@Qualifier("SpringJobTrigger") Trigger springJobTrigger) {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
// 覆盖已存在的任务
bean.setOverwriteExistingJobs(true);
// 延时启动定时任务,避免系统未完全启动却开始执行定时任务的情况
bean.setStartupDelay(15);
// 注册触发器
bean.setTriggers(SpringJobTrigger);
return bean;
}
}
完成上述配置后,启动spring boot就可以出发定时器任务了。而且,仔细观察上面的代码,在执行过程中有WriteService
的spring对象注入,而无需我们自己去自定义JobFactory的Spring对象。
4.持久化
任务持久化需要用到数据库,而初始化数据库的SQL可以从下载的发布版的文件中找到,比如,我在官网的Download页下载了当前版本的Full Distribution:Quartz 2.2.3 .tar.gz
,解压后在quartz-2.2.3\docs\dbTables
能找到初始化脚本,因我用的是MySQL的Innodb引擎,所以我用此脚本tables_mysql_innodb.sql
。
配置
默认情况下,调度器的详情信息会被存储在内存,模式为:RAMJobStore
,而且也不需要填写quartz.properties的配置。然而,如果是持久化的模式,那么quartz.properties就必须填写,因为文件中制定了信息存储模式和数据源信息。
# 线程调度器实例名
org.quartz.scheduler.instanceName = quartzScheduler
# 线程池的线程数,即最多3个任务同时跑
org.quartz.threadPool.threadCount = 3
# 如何存储任务和触发器等信息
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 驱动代理
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 表前缀
org.quartz.jobStore.tablePrefix = qrtz_
# 数据源
org.quartz.jobStore.dataSource = quartzDataSource
# 是否集群
org.quartz.jobStore.isClustered = false
# 数据源
# 驱动
org.quartz.dataSource.quartzDataSource.driver = com.mysql.cj.jdbc.Driver
# 连接URL
org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8&useSSL=true&&serverTimezone=Asia/Shanghai
# 用户名
org.quartz.dataSource.quartzDataSource.user = root
# 密码
org.quartz.dataSource.quartzDataSource.password = 123456
# 最大连接数
org.quartz.dataSource.quartzDataSource.maxConnections = 5
其他内容和RAMJobStore
模式相同。
Quartz:基本用法总结的更多相关文章
- Spring 定时器Quartz的用法
Spring定时器Quartz的用法也很简单,需要引入quartz-all-1.5.2.jar java代码如下: package com.coalmine.desktop; import java. ...
- Java EE学习--Quartz基本用法
新浪博客完全不适合写技术类文章.本来是想找一个技术性的博客发发自己最近学的东西,发现博客园起源于咱江苏,一个非常质朴的网站,行,咱要养成好习惯,以后没事多总结总结经验吧.很多时候都在网上搜索别人的总结 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(85)-Quartz 作业调度用法详解二
前言 上一节我们学习了Quartz的基本用法 这一节学习通过XML配置的形式来执行任务 这一节主要认识一些属性,为下一步打基础 代码下载:链接:http://pan.baidu.com/s/1ge6j ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(84)-Quartz 作业调度用法详解一
前言 我从Quartz2.0开始使用,并对其进行了封装了界面,可以参考 http://www.cnblogs.com/ymnets/p/5065154.html 最近拿出来进行了优化,并升级到最新版, ...
- Spring定时器Quartz的用法
首先导入需要的两个jar: spring-context-support-4.1.1.RELEASE.jar quartz-2.2.1.jar 1.创建两个类: 2. QuartzConfigurat ...
- Java EE学习——Quartz的Cron表达式
经历过低谷后,还是要好好学习,越失落会越来越落后. 今天写一下Cron表达式的用法,虽然是之前自己写的,也过了挺长一段时间,这次就拿出来作为回顾吧. Cron表达式是Quartz的精髓(个人觉得),比 ...
- 用Quartz处理定时执行的任务
这次做的项目中,有一部分功能需要实现定时执行.呃,这样说可能有点笼统,打个比方吧.例如用户在登录的时候,连续输错3次密码后,系统会将该用户冻结,不再允许该用户登录系统,等到了晚上零晨时分,再为所有被冻 ...
- Spring中Quartz调度器的使用
一.Quartz的特点 * 按作业类的继承方式来分,主要有以下两种: 1.作业类继承org.springframework.scheduling.quartz.QuartzJobBean类的方式 2. ...
- quartz开源插件(定时心跳后台执行)
定时心跳,一般应用场景都是服务或者exe控制台程序来搜集数据推送等,供其他页面来调用或者向服务推送等,但又不限于此. 1.先来介绍下quartz吧. 2.quartz用法: 3.我写个小例子来巩固下q ...
- java web定时任务---quartz
写在前面: 前面有简单的记录下Timer定时的用法,但是在此次项目中,选择的是quartz来完成定时操作任务的.两者都可以完成定时操作,但是spring可以整合quartz,并且配置起来也比较简便,还 ...
随机推荐
- Promise对象 异步编程
Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大.所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是 ...
- xctf-web supersqli
单引号注入,用order by查到了两个column.用union select的时候发现select关键字被过滤了 用分号尝试堆叠注入显示出了两张表 分别查询字段 flag在表19198109311 ...
- 前端学习(四):body标签(二)
进击のpython ***** 前端学习--body标签 接着上一节,我们看一下还有没有什么网址 果不其然,在看到新闻类的网址的时候 我们发现还有许多的不一样的东西! 使用ul,添加新闻信息列表 这个 ...
- Spring Security 实战干货:图解用户是如何登录的
1. 前言 欢迎阅读Spring Security 实战干货系列文章,在集成Spring Security安全框架的时候我们最先处理的可能就是根据我们项目的实际需要来定制注册登录了,尤其是Http登录 ...
- Kubernetes/K8s CKA认证全套实训视频教程下载
地址: 链接:https://pan.baidu.com/s/1bwEUZTCVzqM3mGjrlISbcg 提取码:r1kx 目录: 目录: │ 1-1.kubernetes理论教程 - 云原生技术 ...
- SpringMV学习之拦截器的简单使用
一.拦截器介绍 我们知道SpringMVC的拦截器(Interceptor)非常重要,尤其在Web应用中使用非常广泛,其功能类似于过滤器,用于拦截用户请求并作相应的处理.我们使用比较多的场景是系统登陆 ...
- 你一个 F12 能秒我?
参考文章 思路决定成败:F12给了我黑色的眼睛我却用它来挖洞 前言 了解大佬的思路才知道自己现在学的到底有多浅. 还没有到点,我就已经进入状态了,生而为人,我很抱歉. F12 的骚操作 Element ...
- Python匿名函数_return语句
Python匿名函数: 使用 lambda 关键字创建匿名函数: lambda 定义的函数只是一个表达式,而不是代码块 lambda 函数拥有自己的命名空间,不能够访问参数列表之外的 或 全局命名空间 ...
- 撸了一个 Feign 增强包
前言 最近准备将公司的一个核心业务系统用 Java 进行重构,大半年没写 Java ,JDK 都更新到 14 了,考虑到稳定性等问题最终还是选择的 JDK11. 在整体架构选型时,由于是一个全新的系统 ...
- Ynoi专练
为了练习分块 莫队 bitset黑科技 我会写几道Ynoi 放到这里. bitset 每一位占1bit int 每一位占 4 bitye bool占1 bitye long long 8bitye L ...