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匹配了。
關於測試代碼的其他配置可移步參考本系列前面的文章,里面都有詳細的配置實例講解