1、quartz導讀:https://blog.csdn.net/u010648555/article/details/54863144
2、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 ,代表有狀態任務。 有狀態任務不可併發,前次任務沒有執行完,後面任務處於阻塞等到。
JobDataMap:存儲Job和Trigger的運行時數據,其實就是Map對象
JobExecutionContext:定義Job執行時的上下文,可從中獲取Job和Trigger的數據,包括上面的JobDataMap
ScheduleBuilder:時間規則的接口,實現類有SimpleScheduleBuilder,CronScheduleBuilder
SimpleScheduleBuilder:簡單時間規則,包含了interval、repeatCount屬性,用來構造SimpleTriggerImpl
CronScheduleBuilder:cron表達式規則,包含了cronExpression屬性,用來構造CronTriggerImpl
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 :提供用於獲取調度程序實例的客戶端可用句柄的機制。
3、編程思想
調用示例
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.1、工廠模式
工廠模式用來生產產品,有兩個關鍵的角色:產品及工廠
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.2、生成器模式
生成器模式相當於類的構造器,當類中有複雜的屬性時可以考慮這種模式,例如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.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);
}
}
}