Quartz任务调度(4)JobListener分版本超详细解析

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
简介: <div class="markdown_views"><p>在<a href="http://blog.csdn.net/qwe6112071/article/details/50966660">《spring学习笔记(15)趣谈spring 事件:实现业务逻辑解耦,异步调用提升用户体验》</a>我们通过实例分析讲解了spring的事件机制,或许你会觉得其中的配置略显繁琐,

《spring学习笔记(15)趣谈spring 事件:实现业务逻辑解耦,异步调用提升用户体验》我们通过实例分析讲解了spring的事件机制,或许你会觉得其中的配置略显繁琐,而在Quartz框架中,它为我们集成了强大的事件机制,轻松地帮助我们在任务调度中完成各类辅佐操作,高内聚而耦合。
相对spring的事件实现,quartz这边简化了许多,我们只需:
1. 自定义监听器接口实现类
2. 向scheduler中注册监听器实现类

只需以上两步即可我完成我们的事件监听。对于监听器实现类中,可能有些方法不是我们需要的,这时候我们只需给出空实现即可。在Quartz中,监听器类型主要分为三种,和Quartz三个核心类相对应:JobListener,TriggerListener,SchedulerListener。下面我们先分析JobListener的使用方法。其他两种监听器留待后面系列文章详解

JobListener

我们的jobListener实现类必须实现其以下方法:

方法 说明
getName() getName() 方法返回一个字符串用以说明 JobListener 的名称。对于注册为全局的监听器,getName() 主要用于记录日志,对于由特定 Job 引用的 JobListener,注册在 JobDetail 上的监听器名称必须匹配从监听器上 getName() 方法的返回值。
jobToBeExecuted() Scheduler 在 JobDetail 将要被执行时调用这个方法。
jobExecutionVetoed() Scheduler 在 JobDetail 即将被执行,但又被 TriggerListener 否决了时调用这个方法。
jobWasExecuted() Scheduler 在 JobDetail 被执行之后调用这个方法。

接下来我们以《Quartz任务调度(1)概念例析快速入门》一文中的定时扒取新闻任务和获得最热新闻任务为例,分析我们的监听器方法。

1. 自定义监听器接口实现类

public class MyJobListener implements JobListener {

    @Override//相当于为我们的监听器命名
    public String getName() {
        return "myJobListener";
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        System.out.println(getName() + "触发对"+context.getJobDetail().getJobClass()+"的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录");
    }

    @Override//“否决JobDetail”是在Triiger被其相应的监听器监听时才具备的能力
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("被否决执行了,可以做些日志记录。");
    }

    @Override
    public void jobWasExecuted(JobExecutionContext context,
            JobExecutionException jobException) {
        System.out.println(getName() + "触发对"+context.getJobDetail().getJobClass()+"结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作");

    }

}

2. 在scheduler中注册监听器

这里有两种方式,一种是注册为全局监听器,对所有的JobDetail都有效,另一种是注册为针对特定JobDetail的局部监听器。针对不同的版本,有不同的配置方式

1. 准备工作

在测试中我们用到工作实现类为

public class PickNewsJob implements Job {

    @Override
    public void execute(JobExecutionContext jec) throws JobExecutionException {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println("在" + sdf.format(new Date()) + "扒取新闻");
    }
}

public class GetHottestJob implements Job {

    @Override
    public void execute(JobExecutionContext jec) throws JobExecutionException {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println("在" + sdf.format(new Date()) +"根据文章的阅读量和评论量来生成我们的最热文章列表");
    }

}

2. 1.x版本配置

在1.+版本中,我们可以通过如下代码监听job

/**********局部监听器配置**********/
JobListener myJobListener = new MyJobListener();
pickNewsJob.addJobListener("myJobListener");//这里的名字和myJobListener中getName()方法的名字一样
scheduler.addJobListener(myJobListener);//向scheduler注册我们的监听器
/*********全局监听器配置************/
JobListener myJobListener = new MyJobListener();
scheduler.addGlobalJobListener(myJobListener);//直接添加为全局监听器

下面是我们的完整测试代码:

public static void main(String args[]) throws SchedulerException {
    JobDetail pickNewsJob =new JobDetail("job1", "jgroup1", PickNewsJob.class); 
    JobDetail getHottestJob =new JobDetail("job2", "jgroup2", GetHottestJob.class);
    SimpleTrigger pickNewsTrigger = new SimpleTrigger("trigger1", "group1",1,2000);
    SimpleTrigger getHottestTrigger = new SimpleTrigger("trigger2", "group2",1,3000);

    SchedulerFactory schedulerFactory = new StdSchedulerFactory();
    Scheduler scheduler = schedulerFactory.getScheduler();

    /**********局部监听器配置**********/
    JobListener myJobListener = new MyJobListener();
    pickNewsJob.addJobListener("myJobListener");//这里的名字和myJobListener中getName()方法的名字一样
    scheduler.addJobListener(myJobListener);//向scheduler注册我们的监听器
    /*********全局监听器配置************/
//      JobListener myJobListener = new MyJobListener();
//      scheduler.addGlobalJobListener(myJobListener);//直接添加为全局监听器

    scheduler.scheduleJob(pickNewsJob,pickNewsTrigger);
    scheduler.scheduleJob(getHottestJob,getHottestTrigger);

    scheduler.start();

}

现在是使用局部监听器的配置,运行程序,控制台打印:

myJobListener触发对class tool.job.PickNewsJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
在11:18:31扒取新闻
在11:18:31根据文章的阅读量和评论量来生成我们的最热文章列表————————从这里我们可以看出两个工作是异步进行的
myJobListener触发对class tool.job.PickNewsJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
myJobListener触发对class tool.job.PickNewsJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
在11:18:33扒取新闻
myJobListener触发对class tool.job.PickNewsJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
在11:18:34根据文章的阅读量和评论量来生成我们的最热文章列表
我们细心观察还会发现,我们两个工作都运行了三次,但我们在配置触发器时,repeatCount都是设为2。这说明我们的任务调度特点是:主执行了1次,重复了2次,于是共执行3(1+repeatCount)次。

如果我们注释掉局部监听代码,启用全局监听,会看到控制台打印:

myJobListener触发对class tool.job.PickNewsJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
myJobListener触发对class tool.job.GetHottestJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
在11:25:41扒取新闻
在11:25:41根据文章的阅读量和评论量来生成我们的最热文章列表
myJobListener触发对class tool.job.GetHottestJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
myJobListener触发对class tool.job.PickNewsJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
myJobListener触发对class tool.job.PickNewsJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
在11:25:43扒取新闻
myJobListener触发对class tool.job.PickNewsJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
myJobListener触发对class tool.job.GetHottestJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
在11:25:44根据文章的阅读量和评论量来生成我们的最热文章列表
myJobListener触发对class tool.job.GetHottestJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
即我们的两个任务都被监听了

3. 2.x版本配置

在2.+版本中,引入了**org.quartz.ListenerManager和org.quartz.Matcher
**来对我们的监听器进行更细粒度的管理配置

1. ListenerManager

我们通过ListenerManager向scheduler中添加我们的监听器。它针对JobDetail的常用方法有:
1. public void addJobListener(JobListener jobListener)
添加全局监听器,即所有JobDetail都会被此监听器监听
2. public void addJobListener(JobListener jobListener, Matcher matcher)
添加带条件匹配的监听器,在matcher中声明我们的匹配条件
3. public void addJobListener(JobListener jobListener, Matcher … matchers)
添加附带不定参条件陪陪的监听器
4. public boolean removeJobListener(String name)
根据名字移除JobListener
5. public List getJobListeners()
获取所有的监听器
6. public JobListener getJobListener(String name)
根据名字获取监听器

2. matcher

我们通过matcher让不同的监听器监听不同的任务。它有很多实现类,先逐一分析如下:

1. KeyMatcher<JobKey>

根据JobKey进行匹配,每个JobDetail都有一个对应的JobKey,里面存储了JobName和JobGroup来定位唯一的JobDetail。它的常用方法有:

    /************构造Matcher方法************/
    KeyMatcher<JobKey> keyMatcher = KeyMatcher.keyEquals(pickNewsJob.getKey());//构造匹配pickNewsJob中的JobKey的keyMatcher。

    /*********使用方法************/
    scheduler.getListenerManager().addJobListener(myJobListener, keyMatcher);//通过这句完成我们监听器对pickNewsJob的唯一监听
2. GroupMatcher

根据组名信息匹配,它的常用方法有:

    GroupMatcher<JobKey> groupMatcher = GroupMatcher.jobGroupContains("group1");//包含特定字符串
            GroupMatcher.groupEndsWith("oup1");//以特定字符串结尾
            GroupMatcher.groupEquals("jgroup1");//以特定字符串完全匹配
            GroupMatcher.groupStartsWith("jgou");//以特定字符串开头
3. AndMatcher

对两个匹配器取交集,实例如下:

KeyMatcher<JobKey> keyMatcher = KeyMatcher.keyEquals(pickNewsJob.getKey());
GroupMatcher<JobKey> groupMatcher = GroupMatcher.jobGroupContains("group1");
AndMatcher<JobKey> andMatcher = AndMatcher.and(keyMatcher,groupMatcher);//同时满足两个入参匹配
4. OrMatcher

对两个匹配器取并集,实例如下:

OrMatcher<JobKey> orMatcher = OrMatcher.or(keyMatcher, groupMatcher);//满足任意一个即可
5. EverythingMatcher

局部全局匹配,它有两个构造方法:

EverythingMatcher.allJobs();//对全部JobListener匹配
EverythingMatcher.allTriggers();//对全部TriggerListener匹配

下面是我们的完整测试测序:

public static void main(String args[]) throws SchedulerException {
    final JobDetail pickNewsJob = JobBuilder.newJob(PickNewsJob.class)
            .withIdentity("job1", "jgroup1").build();
    JobDetail getHottestJob = JobBuilder.newJob(GetHottestJob.class)
            .withIdentity("job2", "jgroup2").build();
    SimpleTrigger pickNewsTrigger = TriggerBuilder
            .newTrigger()
            .withIdentity("trigger1","tgroup1")
            .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(2, 1)).startNow()
            .build();
    SimpleTrigger getHottestTrigger = TriggerBuilder
            .newTrigger()
            .withIdentity("trigger2","tgroup2")
            .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(2, 2)).startNow()
            .build();
    Scheduler scheduler = new StdSchedulerFactory().getScheduler();
    JobListener myJobListener = new MyJobListener();
    KeyMatcher<JobKey> keyMatcher = KeyMatcher.keyEquals(pickNewsJob.getKey());
    scheduler.getListenerManager().addJobListener(myJobListener, keyMatcher);
    scheduler.scheduleJob(pickNewsJob, pickNewsTrigger);
    scheduler.scheduleJob(getHottestJob,getHottestTrigger);
    scheduler.start();
}

运行程序,我们得到下列打印信息:

myJobListener触发对class tool.job.PickNewsJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
根据文章的阅读量和评论量来生成我们的最热文章列表
在12:48:58扒取新闻
myJobListener触发对class tool.job.PickNewsJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
myJobListener触发对class tool.job.PickNewsJob的开始执行的监听工作,这里可以完成任务前的一些资源准备工作或日志记录
在12:48:59扒取新闻
myJobListener触发对class tool.job.PickNewsJob结束执行的监听工作,这里可以进行资源销毁工作或做一些新闻扒取结果的统计工作
根据文章的阅读量和评论量来生成我们的最热文章列表
显然,myJobListener只和我们的PickNewsJob匹配了。
关于测试代码的其他配置可移步参考本系列前面的文章,里面都有详细的配置实例讲解

源码下载

关于本节测试源码内容可到https://github.com/jeanhao/spring下quartzEvent文件夹下载

目录
相关文章
|
2月前
|
Web App开发 前端开发
Chrome 浏览器插件 V3 版本 Manifest.json 文件中 Action 的类型(Types)、方法(Methods)和事件(Events)的属性和参数解析
Chrome 浏览器插件 V3 版本 Manifest.json 文件中 Action 的类型(Types)、方法(Methods)和事件(Events)的属性和参数解析
186 0
|
11天前
|
域名解析 存储 缓存
HTTP请求流程概览:浏览器构建请求行含方法、URL和版本;检查缓存;解析IP与端口
【6月更文挑战第23天】 HTTP请求流程概览:浏览器构建请求行含方法、URL和版本;检查缓存;解析IP与端口;TCP连接(HTTP/1.1可能需排队);三次握手;发送请求头与体;服务器处理并返回响应;TCP连接可能关闭或保持;浏览器接收并显示响应,更新缓存。HTTP版本间有差异。
26 5
|
2月前
|
JavaScript 前端开发 Linux
|
2月前
|
存储 关系型数据库 MySQL
MVCC:深入解析多版本并发控制机制
【4月更文挑战第20天】MVCC是数据库并发控制的关键技术,通过保存数据多个版本,使读写操作无锁并发,减少锁竞争,提高并发性能。它保证事务看到一致数据快照,避免并发问题,并支持事务回滚与恢复。MVCC广泛应用于PostgreSQL、InnoDB等,提供时间旅行查询和无锁读等功能,对于构建高性能、高并发数据库系统至关重要。
44 13
|
2月前
|
搜索推荐 算法 索引
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
|
2月前
|
网络协议 Windows
Windows Server 各版本搭建 DNS 服务器实现域名正反向解析
Windows Server 各版本搭建 DNS 服务器实现域名正反向解析
|
2月前
|
算法 程序员 C++
【Qt 焦点】深入解析 焦点枚举 Qt::FocusPolicy 属性及其在不同版本中的应用
【Qt 焦点】深入解析 焦点枚举 Qt::FocusPolicy 属性及其在不同版本中的应用
135 0
|
2月前
|
SQL JSON 关系型数据库
【SQL编程】MySQL 5.7.28 版本使用 SQL 直接解析 JSON 字符串(判断是否是合法JSON类型+文本深度+文本长度+值类型+keys获取+值获取+不同深度数据获取)
【SQL编程】MySQL 5.7.28 版本使用 SQL 直接解析 JSON 字符串(判断是否是合法JSON类型+文本深度+文本长度+值类型+keys获取+值获取+不同深度数据获取)
74 0
|
7月前
|
供应链 安全 JavaScript
版本升级 | v3.0.0卷起来了!多种特殊情况解析轻松拿捏!
OpenSCA解析引擎全方位优化,v3.0.0版本正式发布啦
37 0
版本升级 | v3.0.0卷起来了!多种特殊情况解析轻松拿捏!
|
2月前
|
设计模式 Oracle 关系型数据库
二十三种设计模式全面解析-适配器模式的妙用:异构数据库和不同版本API的完美兼容!
二十三种设计模式全面解析-适配器模式的妙用:异构数据库和不同版本API的完美兼容!

推荐镜像

更多