一、quartz導讀:https://blog.csdn.net/u010648555/article/details/54863144java
二、quarz核心類:編程
JobDetail:傳遞給定做業實例的詳細信息屬性,JobBuilder使用Builder模式構造JobDetail對象,JobDetail實現類是JobDetailImpl設計模式
JobBuilder:能夠當作JobDetail的構造器,JobDetailImpl的屬性name、group標識惟一性,jobClass標識job類名,jobDataMap定義job的data,可在運行時獲取,durability指定JobDetail沒有Trigger關聯時是否存儲併發
Job:要由表示要執行的「做業」的類實現的接口。只有一個方法 void execute(jobExecutionContext context) (jobExecutionContext 提供調度上下文各類信息,運行時數據保存在jobDataMap中) ,Job有個子接口StatefulJob ,表明有狀態任務。 有狀態任務不可併發,前次任務沒有執行完,後面任務處於阻塞等到。app
JobDataMap:存儲Job和Trigger的運行時數據,其實就是Map對象dom
JobExecutionContext:定義Job執行時的上下文,可從中獲取Job和Trigger的數據,包括上面的JobDataMapoop
ScheduleBuilder:時間規則的接口,實現類有SimpleScheduleBuilder,CronScheduleBuilderui
SimpleScheduleBuilder:簡單時間規則,包含了interval、repeatCount屬性,用來構造SimpleTriggerImplthis
CronScheduleBuilder:cron表達式規則,包含了cronExpression屬性,用來構造CronTriggerImpl.net
Trigger:具備全部觸發器通用屬性的基本接口,描述了job執行的時間出發規則。 - 使用TriggerBuilder實例化實際觸發器。
TriggerBuilder:能夠當作Trigger的構造器,其實SimpleScheduleBuilder、CronScheduleBuilder也能夠當作Trigger的構造器
Scheduler:這是Quartz Scheduler的主要接口,表明一個獨立運行容器。調度程序維護JobDetails和觸發器的註冊表。 一旦註冊,調度程序負責執行做業,當他們的相關聯的觸發器觸發(當他們的預約時間到達時),實現類是StdScheduler,這是一個代理類,核心的是QuartzScheduler
QuartzScheduler:這是Quartz的核心,它是org.quartz.Scheduler接口的間接實現,包含調度org.quartz.Jobs,註冊org.quartz.JobListener實例等的方法。
QuartzSchedulerResources:包含建立QuartzScheduler實例所需的全部資源(JobStore,ThreadPool等)。
QuartzSchedulerThread:負責執行向QuartzScheduler註冊的觸發Trigger的工做的線程。
ThreadPool:Scheduler使用一個線程池做爲任務運行的基礎設施,任務經過共享線程池中的線程提供運行效率。
JobStore: 經過類實現的接口,這些類要爲org.quartz.core.QuartzScheduler的使用提供一個org.quartz.Job和org.quartz.Trigger存儲機制。做業和觸發器的存儲應該以其名稱和組的組合爲惟一性。
SchedulerFactory :提供用於獲取調度程序實例的客戶端可用句柄的機制。
三、編程思想
調用示例
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); // 獲取一個調度器 Scheduler sched = schedFact.getScheduler(); JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build(); // 每兩秒執行 DailyTimeIntervalTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").withSchedule( DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule().withInterval(2, DateBuilder.IntervalUnit.SECOND) ).build(); sched.scheduleJob(job, trigger); // 調度啓動 sched.start();
3.一、工廠模式
工廠模式用來生產產品,有兩個關鍵的角色:產品及工廠
StdSchedulerFactory工廠實現SchedulerFactory接口,生產Scheduler產品
public Scheduler getScheduler() throws SchedulerException { //判斷cfg是否爲空,若是爲空,則尚未初始化,須要根據配置文件初始化配置 if (cfg == null) { initialize(); } //根據SchedulerName獲取Scheduler SchedulerRepository schedRep = SchedulerRepository.getInstance(); Scheduler sched = schedRep.lookup(getSchedulerName()); //若是獲取了沒有銷燬,則直接返回,若是銷燬了直接remove掉 if (sched != null) { if (sched.isShutdown()) { schedRep.remove(getSchedulerName()); } else { return sched; } } //此處須要構造Scheduler實例 sched = instantiate(); return sched; }
SimpleJobFactory工廠實現JobFactory接口,生產Job產品,上面經過JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build()建立了一個JobDetail並註冊到調度程序中,在運行時其實是建立了一個HelloJob的任務運行
public class SimpleJobFactory implements JobFactory { private final Logger log = LoggerFactory.getLogger(getClass()); protected Logger getLog() { return log; } public Job newJob(TriggerFiredBundle bundle, Scheduler Scheduler) throws SchedulerException { JobDetail jobDetail = bundle.getJobDetail(); Class<? extends Job> jobClass = jobDetail.getJobClass(); try { if(log.isDebugEnabled()) { log.debug( "Producing instance of Job '" + jobDetail.getKey() + "', class=" + jobClass.getName()); } return jobClass.newInstance(); } catch (Exception e) { SchedulerException se = new SchedulerException( "Problem instantiating class '" + jobDetail.getJobClass().getName() + "'", e); throw se; } } }
3.二、生成器模式
生成器模式至關於類的構造器,當類中有複雜的屬性時能夠考慮這種模式,例如quartz中Trigger,抽象類AbstractTrigger封裝了trigger的共同屬性,派生類SimpleTriggerImpl和CronTriggerImpl封裝了trigger的觸發時間規則,前者是固定時間間隔簡單觸發器,後者是cron表達式的複雜觸發器,常規思路是根據場景調用SimpleTriggerImpl和CronTriggerImpl不一樣的構造方法生成對象,但這樣就會有不少構造方法,並且調用很複雜。
public abstract class AbstractTrigger<T extends Trigger> implements OperableTrigger { private static final long serialVersionUID = -3904243490805975570L; private String name; private String group = Scheduler.DEFAULT_GROUP; private String jobName; private String jobGroup = Scheduler.DEFAULT_GROUP; private String description; private JobDataMap jobDataMap; private boolean volatility = false; // still here for serialization backward compatibility private String calendarName = null; private String fireInstanceId = null; private int misfireInstruction = MISFIRE_INSTRUCTION_SMART_POLICY; private int priority = DEFAULT_PRIORITY; private transient TriggerKey key = null; }
public class SimpleTriggerImpl extends AbstractTrigger<SimpleTrigger> implements SimpleTrigger, CoreTrigger { private static final long serialVersionUID = -3735980074222850397L; private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100; private Date startTime = null; private Date endTime = null; private Date nextFireTime = null; private Date previousFireTime = null; private int repeatCount = 0; private long repeatInterval = 0; private int timesTriggered = 0; private boolean complete = false; }
public class CronTriggerImpl extends AbstractTrigger<CronTrigger> implements CronTrigger, CoreTrigger { private static final long serialVersionUID = -8644953146451592766L; protected static final int YEAR_TO_GIVEUP_SCHEDULING_AT = CronExpression.MAX_YEAR; private CronExpression cronEx = null; private Date startTime = null; private Date endTime = null; private Date nextFireTime = null; private Date previousFireTime = null; private transient TimeZone timeZone = null; }
quartz在這裏封裝了三個構造器生成trigger對象,TriggerBuilder構造AbstractTrigger屬性,抽象類ScheduleBuilder派生類SimpleScheduleBuilder和CronScheduleBuilder構造SimpleTriggerImpl和CronTriggerImpl屬性
public class TriggerBuilder<T extends Trigger> { private TriggerKey key; private String description; private Date startTime = new Date(); private Date endTime; private int priority = Trigger.DEFAULT_PRIORITY; private String calendarName; private JobKey jobKey; private JobDataMap jobDataMap = new JobDataMap(); private ScheduleBuilder scheduleBuilder = null; }
public class SimpleScheduleBuilder extends ScheduleBuilder<SimpleTrigger> { private long interval = 0; private int repeatCount = 0; private int misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_SMART_POLICY; }
public class CronScheduleBuilder extends ScheduleBuilder<CronTrigger> { private String cronExpression; private TimeZone tz; private int misfireInstruction = CronTrigger.MISFIRE_INSTRUCTION_SMART_POLICY; }
每一個xxxBuilder的方法,提供了屬性設置方法,並返回對象自己,這樣就能夠直接在設置某個屬性後直接設置其餘屬性,例如newTrigger() .withIdentity(triggerKey("myTrigger","myTriggerGroup")).withSchedule(simpleSchedule() .withIntervalInHours(1) .repeatForever()).startAt(futureDate(10, MINUTES)).build(),build方法生成具體的對象。這裏SimpleScheduleBuilder生成SimpleTriggerImpl對象,CronScheduleBuilder生成CronTriggerImpl對象,TriggerBuilder設置父類的屬性。
public class TriggerBuilder<T extends Trigger> { private TriggerBuilder() { } public static TriggerBuilder<Trigger> newTrigger() { return new TriggerBuilder<Trigger>(); } public TriggerBuilder<T> withIdentity(String name) { key = new TriggerKey(name, null); return this; } public TriggerBuilder<T> withIdentity(String name, String group) { key = new TriggerKey(name, group); return this; } public T build() { if(scheduleBuilder == null) scheduleBuilder = SimpleScheduleBuilder.simpleSchedule(); MutableTrigger trig = scheduleBuilder.build(); trig.setCalendarName(calendarName); trig.setDescription(description); trig.setEndTime(endTime); if(key == null) key = new TriggerKey(Key.createUniqueName(null), null); trig.setKey(key); if(jobKey != null) trig.setJobKey(jobKey); trig.setPriority(priority); trig.setStartTime(startTime); if(!jobDataMap.isEmpty()) trig.setJobDataMap(jobDataMap); return (T) trig; } }
3.三、觀察者模式
觀察者設計模式適合多種對象跟蹤一個對象數據變化的程序結構問題,有一個稱做「主題」的對象和若干個「觀察者」的對象,所以觀察者模式涉及兩個角色:主題和觀察者,觀察者相似於監控的角色,quartz能夠註冊調度程序、任務、觸發器的觀察者,前面講到的Scheduler實現類StdScheduler是一個代理類,內部包含了QuartzScheduler對象,咱們在調用示例中使用sched.scheduleJob(job, trigger)向調度程序註冊了任務和觸發器,如今咱們想監控調度程序任務運行的狀況,有什麼辦法嗎,答案是有的,咱們先看看QuartzScheduler屬性
public class QuartzScheduler implements RemotableQuartzScheduler { private QuartzSchedulerResources resources; private QuartzSchedulerThread schedThread; private ThreadGroup threadGroup; private SchedulerContext context = new SchedulerContext(); private ListenerManager listenerManager = new ListenerManagerImpl(); private HashMap<String, JobListener> internalJobListeners = new HashMap<String, JobListener>(10); private HashMap<String, TriggerListener> internalTriggerListeners = new HashMap<String, TriggerListener>(10); private ArrayList<SchedulerListener> internalSchedulerListeners = new ArrayList<SchedulerListener>(10); private JobFactory jobFactory = new SimpleJobFactory(); ExecutingJobsManager jobMgr = null; ErrorLogger errLogger = null; private SchedulerSignaler signaler; private Random random = new Random(); private ArrayList<Object> holdToPreventGC = new ArrayList<Object>(5); private boolean signalOnSchedulingChange = true; private volatile boolean closed = false; private volatile boolean shuttingDown = false; private boolean boundRemotely = false; private QuartzSchedulerMBean jmxBean = null; private Date initialStart = null; private final Timer updateTimer; private final Logger log = LoggerFactory.getLogger(getClass()); }
上面類中listenerManager的屬性正是咱們解決的辦法,那麼怎麼註冊這些觀察者呢,看看ListenerManager,提供了JobListener、TriggerListene、SchedulerListener添加
public interface ListenerManager { public void addJobListener(JobListener jobListener, Matcher<JobKey> ... matchers); public void addJobListener(JobListener jobListener, List<Matcher<JobKey>> matchers); public boolean addJobListenerMatcher(String listenerName, Matcher<JobKey> matcher); public boolean removeJobListenerMatcher(String listenerName, Matcher<JobKey> matcher); public boolean setJobListenerMatchers(String listenerName, List<Matcher<JobKey>> matchers); public List<Matcher<JobKey>> getJobListenerMatchers(String listenerName); public boolean removeJobListener(String name); public List<JobListener> getJobListeners(); public JobListener getJobListener(String name); public void addTriggerListener(TriggerListener triggerListener, Matcher<TriggerKey> ... matchers); public void addTriggerListener(TriggerListener triggerListener, List<Matcher<TriggerKey>> matchers); public boolean addTriggerListenerMatcher(String listenerName, Matcher<TriggerKey> matcher); public boolean removeTriggerListenerMatcher(String listenerName, Matcher<TriggerKey> matcher); public boolean setTriggerListenerMatchers(String listenerName, List<Matcher<TriggerKey>> matchers); public List<Matcher<TriggerKey>> getTriggerListenerMatchers( String listenerName); public boolean removeTriggerListener(String name); public List<TriggerListener> getTriggerListeners(); public TriggerListener getTriggerListener(String name); public void addSchedulerListener(SchedulerListener schedulerListener); public boolean removeSchedulerListener(SchedulerListener schedulerListener); public List<SchedulerListener> getSchedulerListeners(); }
那麼什麼時候註冊這些觀察者呢,quartz在StdSchedulerFactory工廠類的initialize方法中根據配置文件來註冊,程序中開發了JobListener、TriggerListener,能夠再quartz.properties中添加,下面看看StdSchedulerFactory如何註冊的,就是根據配置文件生成JobListener、TriggerListener對象,再調用QuartzScheduler的listenerManager註冊,目前沒有實現SchedulerListener添加
String[] jobListenerNames = cfg.getPropertyGroups(PROP_JOB_LISTENER_PREFIX); JobListener[] jobListeners = new JobListener[jobListenerNames.length]; for (int i = 0; i < jobListenerNames.length; i++) { Properties lp = cfg.getPropertyGroup(PROP_JOB_LISTENER_PREFIX + "." + jobListenerNames[i], true); String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null); if (listenerClass == null) { initException = new SchedulerException( "JobListener class not specified for listener '" + jobListenerNames[i] + "'"); throw initException; } JobListener listener = null; try { listener = (JobListener) loadHelper.loadClass(listenerClass).newInstance(); } catch (Exception e) { initException = new SchedulerException( "JobListener class '" + listenerClass + "' could not be instantiated.", e); throw initException; } try { Method nameSetter = listener.getClass().getMethod("setName", strArg); if(nameSetter != null) { nameSetter.invoke(listener, new Object[] {jobListenerNames[i] } ); } setBeanProps(listener, lp); } catch (Exception e) { initException = new SchedulerException( "JobListener '" + listenerClass + "' props could not be configured.", e); throw initException; } jobListeners[i] = listener; } String[] triggerListenerNames = cfg.getPropertyGroups(PROP_TRIGGER_LISTENER_PREFIX); TriggerListener[] triggerListeners = new TriggerListener[triggerListenerNames.length]; for (int i = 0; i < triggerListenerNames.length; i++) { Properties lp = cfg.getPropertyGroup(PROP_TRIGGER_LISTENER_PREFIX + "." + triggerListenerNames[i], true); String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null); if (listenerClass == null) { initException = new SchedulerException( "TriggerListener class not specified for listener '" + triggerListenerNames[i] + "'"); throw initException; } TriggerListener listener = null; try { listener = (TriggerListener) loadHelper.loadClass(listenerClass).newInstance(); } catch (Exception e) { initException = new SchedulerException( "TriggerListener class '" + listenerClass + "' could not be instantiated.", e); throw initException; } try { Method nameSetter = listener.getClass().getMethod("setName", strArg); if(nameSetter != null) { nameSetter.invoke(listener, new Object[] {triggerListenerNames[i] } ); } setBeanProps(listener, lp); } catch (Exception e) { initException = new SchedulerException( "TriggerListener '" + listenerClass + "' props could not be configured.", e); throw initException; } triggerListeners[i] = listener; }
for (int i = 0; i < jobListeners.length; i++) { qs.getListenerManager().addJobListener(jobListeners[i], EverythingMatcher.allJobs()); } for (int i = 0; i < triggerListeners.length; i++) { qs.getListenerManager().addTriggerListener(triggerListeners[i], EverythingMatcher.allTriggers()); }
那麼什麼時候通知這些觀察者呢,經過下面的代碼咱們能夠看到這些觀察者主要監控任務的執行,即任務執行時
public interface TriggerListener { String getName(); void triggerFired(Trigger trigger, JobExecutionContext context); boolean vetoJobExecution(Trigger trigger, JobExecutionContext context); void triggerMisfired(Trigger trigger); void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode); }
public interface JobListener { String getName(); void jobToBeExecuted(JobExecutionContext context); void jobExecutionVetoed(JobExecutionContext context); void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException); }
public class JobRunShell extends SchedulerListenerSupport implements Runnable { public void run() { qs.addInternalSchedulerListener(this); try { OperableTrigger trigger = (OperableTrigger) jec.getTrigger(); JobDetail jobDetail = jec.getJobDetail(); do { JobExecutionException jobExEx = null; Job job = jec.getJobInstance(); try { begin(); } catch (SchedulerException se) { qs.notifySchedulerListenersError("Error executing Job (" + jec.getJobDetail().getKey() + ": couldn't begin execution.", se); break; } // notify job & trigger listeners... try { if (!notifyListenersBeginning(jec)) { break; } } catch(VetoedException ve) { try { CompletedExecutionInstruction instCode = trigger.executionComplete(jec, null); try { qs.notifyJobStoreJobVetoed(trigger, jobDetail, instCode); } catch(JobPersistenceException jpe) { vetoedJobRetryLoop(trigger, jobDetail, instCode); } complete(true); } catch (SchedulerException se) { qs.notifySchedulerListenersError("Error during veto of Job (" + jec.getJobDetail().getKey() + ": couldn't finalize execution.", se); } break; } long startTime = System.currentTimeMillis(); long endTime = startTime; // execute the job try { log.debug("Calling execute on job " + jobDetail.getKey()); job.execute(jec); endTime = System.currentTimeMillis(); } catch (JobExecutionException jee) { endTime = System.currentTimeMillis(); jobExEx = jee; getLog().info("Job " + jobDetail.getKey() + " threw a JobExecutionException: ", jobExEx); } catch (Throwable e) { endTime = System.currentTimeMillis(); getLog().error("Job " + jobDetail.getKey() + " threw an unhandled Exception: ", e); SchedulerException se = new SchedulerException( "Job threw an unhandled exception.", e); qs.notifySchedulerListenersError("Job (" + jec.getJobDetail().getKey() + " threw an exception.", se); jobExEx = new JobExecutionException(se, false); } jec.setJobRunTime(endTime - startTime); // notify all job listeners if (!notifyJobListenersComplete(jec, jobExEx)) { break; } CompletedExecutionInstruction instCode = CompletedExecutionInstruction.NOOP; // update the trigger try { instCode = trigger.executionComplete(jec, jobExEx); } catch (Exception e) { // If this happens, there's a bug in the trigger... SchedulerException se = new SchedulerException( "Trigger threw an unhandled exception.", e); qs.notifySchedulerListenersError( "Please report this error to the Quartz developers.", se); } // notify all trigger listeners if (!notifyTriggerListenersComplete(jec, instCode)) { break; } // update job/trigger or re-execute job if (instCode == CompletedExecutionInstruction.RE_EXECUTE_JOB) { jec.incrementRefireCount(); try { complete(false); } catch (SchedulerException se) { qs.notifySchedulerListenersError("Error executing Job (" + jec.getJobDetail().getKey() + ": couldn't finalize execution.", se); } continue; } try { complete(true); } catch (SchedulerException se) { qs.notifySchedulerListenersError("Error executing Job (" + jec.getJobDetail().getKey() + ": couldn't finalize execution.", se); continue; } try { qs.notifyJobStoreJobComplete(trigger, jobDetail, instCode); } catch (JobPersistenceException jpe) { qs.notifySchedulerListenersError( "An error occured while marking executed job complete. job= '" + jobDetail.getKey() + "'", jpe); if (!completeTriggerRetryLoop(trigger, jobDetail, instCode)) { return; } } break; } while (true); } finally { qs.removeInternalSchedulerListener(this); } } }