三. 监听 Job 事件
org.quartz.JobListener 接口包含一系列的方法,它们会由 Job 在其生命周期中产生的某些关键事件时被调用。JobListener 可用的方法显示在代码 7.1 中。
代码 7.1. org.quartz.JobListener 接口中的方法
1 2 3 4 5 6 7 8 |
public interface JobListener { public String getName(); public void jobToBeExecuted(JobExecutionContext context); public void jobExecutionVetoed(JobExecutionContext context); public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException); } |
JobListener 接口中的方法用途是十分明了的。然后,我们还是要对他们加以简单说明。
·getName() 方法
getName() 方法返回一个字符串用以说明 JobListener 的名称。对于注册为全局的监听器,getName() 主要用于记录日志,对于由特定 Job 引用的 JobListener,注册在 JobDetail 上的监听器名称必须匹配从监听器上 getName() 方法的返回值。在你看完一些例子之后就会很清楚了。
·jobToBeExecuted() 方法
Scheduler 在 JobDetail 将要被执行时调用这个方法。
·jobExecutionVetoed() 方法
Scheduler 在 JobDetail 即将被执行,但又被 TriggerListener 否决了时调用这个方法。
·jobWasExecuted() 方法
Scheduler 在 JobDetail 被执行之后调用这个方法。
代码 7.2 展示了一个很简单的 JobListener 实现。
代码 7.2. 一个简单的 JobListner 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package org.cavaness.quartzbook.chapter7; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobListener; public class SimpleJobListener implements JobListener { Log logger = LogFactory.getLog(SimpleJobListener.class); public String getName() { return getClass().getSimpleName(); } public void jobToBeExecuted(JobExecutionContext context) { String jobName = context.getJobDetail().getName(); logger.info(jobName + " is about to be executed"); } public void jobExecutionVetoed(JobExecutionContext context) { String jobName = context.getJobDetail().getName(); logger.info(jobName + " was vetoed and not executed()"); } public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { String jobName = context.getJobDetail().getName(); logger.info(jobName + " was executed"); } } |
代码 7.2 中的 JobListener 打印一个日志消息,很明显,只是监听器最基本的用法。你要实现的逻辑完全由你和你的应用需要而定。你也许想在 Job 成功完成后发送一个电子邮件,或者在 Job 被否决后部署另一个。你有在回调方法中执行几乎任何动作的自由。
前面,我们提到过 JobListener (和 TriggerListener) 能注册为全局或非全局的。注意了,我们并不需要事先知道在代码 7.2 中的 JobListener 是一个全局或是非全局的;我们仅仅是实现了接口和提供了监听器方法。代码 7.3 描绘了如何使用代码 7.2 中的 SimpleJobListner 使之注册为一个全局的 JobListener。
代码 7.3. 使用 SimpleJobListener 作为一个全局 JobListener
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
package org.cavaness.quartzbook.chapter7; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cavaness.quartzbook.common.PrintInfoJob; import org.quartz.JobDetail; import org.quartz.JobListener; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_7_3 { static Log logger = LogFactory.getLog(Listing_7_3.class); public static void main(String[] args) { Listing_7_3 example = new Listing_7_3(); try { example.startScheduler(); } catch (SchedulerException ex) { logger.error(ex); } } public void startScheduler() throws SchedulerException { // Create an instance of the factory Scheduler scheduler = null; // Create the scheduler and JobDetail scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = new JobDetail("PrintInfoJob", Scheduler.DEFAULT_GROUP, PrintInfoJob.class); /* * Set up a trigger to start firing now, with no end * date/time, repeat forever and have * 10 secs (10000 ms) between each firing. */ Trigger trigger = TriggerUtils.makeSecondlyTrigger(10); trigger.setName("SimpleTrigger"); trigger.setStartTime(new Date()); // Register the JobDetail and Trigger scheduler.scheduleJob(jobDetail, trigger); // Create and register the global job listener JobListener jobListener = new SimpleJobListener("SimpleJobListener"); scheduler.addGlobalJobListener(jobListener); // Start the scheduler scheduler.start(); logger.info("Scheduler was started at " + new Date()); } } |
代码 7.3 中的代码现在看来是相当直截的。创建了一个 JobDetail 和 Trigger 并注册到了 Scheduler 实例上,这在前面我们已是做过许多次了。
代码 7.2 中的 SimpleJobListener 初始化后通过 Scheduler 调用 addGlobalJobListener() 方法注册为一个全局的 JobListener。最后,启动 Scheduler。
因为我们只配置了单个 Job (PrintInfoJob),我们获得回调也只是那个 JobDetail。不过,假如我们部署了其他 Job,我们也能看到第二个 Job 的回调日志信息,因为这个监听顺是配置为全局的。
·注册非全局的 JobListener
你还能使用代码 7.2 中的 SimpleJobListener 作为一个非全局的 JobListener。要做到这点,你仅需要修改代码 7.3 的 startScheduler() 方法中的代码。代码 7.4 显示了这一需要做的小小的改变。
代码 7.4. 使用 SimpleJobListener 作为非全局的 JobListener
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
package org.cavaness.quartzbook.chapter7; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cavaness.quartzbook.common.PrintInfoJob; import org.quartz.JobDetail; import org.quartz.JobListener; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class Listing_7_4 { static Log logger = LogFactory.getLog(Listing_7_4.class); public static void main(String[] args) { Listing_7_4 example = new Listing_7_4(); try { example.startScheduler(); } catch (SchedulerException ex) { logger.error(ex); } } public void startScheduler() throws SchedulerException { Scheduler scheduler = null; try { // Create the scheduler and JobDetail scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = new JobDetail("PrintInfoJob", Scheduler.DEFAULT_GROUP, PrintInfoJob.class); /* * Set up a trigger to start firing now, with no end * date/time, repeat forever and have * 10 secs (10000 ms) between each firing. */ Trigger trigger = TriggerUtils.makeSecondlyTrigger(10); trigger.setName("SimpleTrigger"); trigger.setStartTime(new Date()); // Create the job listener JobListener jobListener = new SimpleJobListener("SimpleJobListener"); // Register Listener as a nonglobal listener scheduler.addJobListener(jobListener); // Listener set on JobDetail before scheduleJob() jobDetail.addJobListener(jobListener.getName()); // Register the JobDetail and Trigger scheduler.scheduleJob(jobDetail, trigger); // Start the scheduler scheduler.start(); logger.info("Scheduler started at " + new Date()); } catch (SchedulerException ex) { logger.error(ex); } } } |
代码 7.4 很类似于代码 7.4 中的代码。因为 JobListener 是要注册为一个非全局的监听器,你就要调用 Scheduler 的 addJobListener() 方法而不是 addGlobalJobListener() 方法了。对于非全局的 JobListener,它应于任何引用到它的 JobDetail 使用 schedulerJob() 或 addJob() 方法注册之前被注册。
接下来,JobListener 的名字要设置给 JobDetail。注意,设置的不是 JobListener 实例,仅仅是它的名称。这是通过调用 addJobListener() 方法并传入名称来完成的。传递给 addJobListener() 方法的名称必须匹配从监听器的 getName() 方法返回的名称。如果 Scheduler 不能根据名称找到监听器,它会抛出一个 SchedulerException 异常。
最后,启动 Scheduler。
非全局 JobListener 相关步骤的顺序加入一个非全局 JobListener 的步骤必须是依序完成。JobListener 必须首先加入到 Scheduler 中。接着,JobListener 才能够设置给 JobDetail 对象。之后,你就能使用 scheduleJob() 方法安全的把 JobDetail 加入到 Scheduler 中。 |
本文链接 https://yanbin.blog/quartz-job-scheduling-framework-7-2/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
代码:
Listing_7_3 L52:
// Create and register the global job listener
JobListener jobListener =
new SimpleJobListener("SimpleJobListener");
上文中的SimpleJobListener类没有这个带参数的构造函数……
是的,原文里也没有这个构造方法,这是它的一个纰漏。要你自己把这个方法补上,或者用 JobListener jobListener = new SimpleJobListener(); 来构造监听器实例。
重启后异常:org.quartz.SchedulerException: JobListener 'notifyJobListener' not found.
用JobStoreTX保存
代码:
JobListener notify = new NotifyJobListener();
if (scheduler.getJobListener(notify.getName()) == null) {
scheduler.addJobListener(notify);
}
jobDetail.addJobListener(notify.getName());
数据库保存了notify.getName()名字,没办法找到NotifyJobListener类
可否透过joblistener得知job未顺利执行完毕?
若有执行到jobToBeExecuted()而没执行到jobWasExecuted()
那表示job可能出错
但当下无法确定jobWasExecuted是还没执行到(job正在run)
还是已出错了!