OpenSymphony所提供的Quartz是任务调度领域享誉盛名的开源框架。Spring提供了集成Quartz的功能,可以让开发人员以更面向Spring的方式创建基于Quartz的任务调度应用。任务调度本身设计多线程并发、运行时间规则制定及解析、运行现场保持与恢复、线程池维护等诸多方面的工作。如果以自定义线程池的原始方法开发,难点很大。

1.普通JAVA任务

启动基本的Quartz任务包含一下流程:

  1. 创建任务类:实现Job接口的void execute(JobExecutionContext context)方法,定义被执行任务的执行逻辑;
  2. 生成JobDetail对象:通过加载任务类(不是实例)来绑定任务逻辑与任务信息;
  3. 生成Trigger对象:定时器的触发时间有两种方式可以定义,分别是CronSchedule和simpleSchedule()。前者使用正则表达式,后者则是简单封装后的定时器。
  4. 获取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的对象,下面介绍一下思路:

  1. 自定义JobFactory工厂方法,扩展AdaptableJobFactory,重写其createJobInstance方法;
  2. 声明SchedulerFactoryBean,传入自定义的JobFactory工厂方法;
  3. 通过新的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:基本用法总结的更多相关文章

  1. Spring 定时器Quartz的用法

    Spring定时器Quartz的用法也很简单,需要引入quartz-all-1.5.2.jar java代码如下: package com.coalmine.desktop; import java. ...

  2. Java EE学习--Quartz基本用法

    新浪博客完全不适合写技术类文章.本来是想找一个技术性的博客发发自己最近学的东西,发现博客园起源于咱江苏,一个非常质朴的网站,行,咱要养成好习惯,以后没事多总结总结经验吧.很多时候都在网上搜索别人的总结 ...

  3. ASP.NET MVC5+EF6+EasyUI 后台管理系统(85)-Quartz 作业调度用法详解二

    前言 上一节我们学习了Quartz的基本用法 这一节学习通过XML配置的形式来执行任务 这一节主要认识一些属性,为下一步打基础 代码下载:链接:http://pan.baidu.com/s/1ge6j ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(84)-Quartz 作业调度用法详解一

    前言 我从Quartz2.0开始使用,并对其进行了封装了界面,可以参考 http://www.cnblogs.com/ymnets/p/5065154.html 最近拿出来进行了优化,并升级到最新版, ...

  5. Spring定时器Quartz的用法

    首先导入需要的两个jar: spring-context-support-4.1.1.RELEASE.jar quartz-2.2.1.jar 1.创建两个类: 2. QuartzConfigurat ...

  6. Java EE学习——Quartz的Cron表达式

    经历过低谷后,还是要好好学习,越失落会越来越落后. 今天写一下Cron表达式的用法,虽然是之前自己写的,也过了挺长一段时间,这次就拿出来作为回顾吧. Cron表达式是Quartz的精髓(个人觉得),比 ...

  7. 用Quartz处理定时执行的任务

    这次做的项目中,有一部分功能需要实现定时执行.呃,这样说可能有点笼统,打个比方吧.例如用户在登录的时候,连续输错3次密码后,系统会将该用户冻结,不再允许该用户登录系统,等到了晚上零晨时分,再为所有被冻 ...

  8. Spring中Quartz调度器的使用

    一.Quartz的特点 * 按作业类的继承方式来分,主要有以下两种: 1.作业类继承org.springframework.scheduling.quartz.QuartzJobBean类的方式 2. ...

  9. quartz开源插件(定时心跳后台执行)

    定时心跳,一般应用场景都是服务或者exe控制台程序来搜集数据推送等,供其他页面来调用或者向服务推送等,但又不限于此. 1.先来介绍下quartz吧. 2.quartz用法: 3.我写个小例子来巩固下q ...

  10. java web定时任务---quartz

    写在前面: 前面有简单的记录下Timer定时的用法,但是在此次项目中,选择的是quartz来完成定时操作任务的.两者都可以完成定时操作,但是spring可以整合quartz,并且配置起来也比较简便,还 ...

随机推荐

  1. SpringBoot集成Dubbo+Zookeeper

    目录 Spring版本 dubbo_zookeeper负责定义接口 dubbo_provider 服务提供者 dubbo_consumer服务使用者 Spring版本 不知道为啥,新创建的Spring ...

  2. 遍历多个 txt 文件进行获取值

    import random def load_config(path): with open(path,'r') as tou: return [line for line in tou.readli ...

  3. PHP fnmatch() 函数

    定义和用法 fnmatch() 函数根据指定的模式来匹配文件名或字符串. 语法 fnmatch(pattern,string,flags) 参数 描述 pattern 必需.规定要检索的模式. str ...

  4. MediaStreamConstraints对象

    MediaStreamConstraints对象作用是在调用getUserMedia()时用于指定应在返回的MediaStream中包括哪些轨道,以及(可选)为这些轨道的设置约束. 属性 audio布 ...

  5. luogu P3761 [TJOI2017]城市 树的直径 bfs

    LINK:城市 谢邀,学弟说的一道毒瘤题. 没有真正的省选题目毒瘤 或者说 写O(n)的做法确实毒瘤. 这里给一个花20min就写完的非常好写的暴力. 容易想到枚举哪条边删掉 删掉之后考虑在哪两个点上 ...

  6. Sharding-JDBC实现水平拆分-单库分表

    参考资料:猿天地   https://mp.weixin.qq.com/s/901rNhc4WhLCQ023zujRVQ 作者:尹吉欢 当单表的数量急剧上升,超过了1千万以上,这个时候就要对表进行水平 ...

  7. Shiro探索1. Realm

    1. Realm 是什么?汉语意思:领域,范围:王国:这个比较抽象: 简单一点就是:Realm 用来对用户进行认证和角色授权的 再简单一点,一个用户怎么判断它有没有登陆?这个用户是什么角色有哪些权限? ...

  8. 解决Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: no jogl in java.library.path问题

    首先要把jonl.jar和gluegen.jar导入到eclipse中,然后把解压后的4个.dll文件也导入到eclipse中 具体操作: jonl文件下载地址 链接:https://pan.baid ...

  9. IDEA中配置Maven

    目录 一.IDEA 集成 Maven 二.IDEA 创建 Maven 版 java 工程 1. 创建 maven 版 java 工程 2. 填写 maven 工程的坐标 3. 填写工程名和存储路径 4 ...

  10. 前端面试 vue 部分 (3)——v-show和v-if的区别

    v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景: v-show 则适用于需要非常频繁切换条件的场景. v-if 是条件渲染,如果在初始渲染时条件为假,则什么也不做--直到条件第一次变为 ...