Quartz任务调度(4)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匹配了。
关于测试代码的其他配置可移步参考本系列前面的文章,里面都有详细的配置实例讲解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理