首先quartz中有三個重要的模塊,定時任務Job、觸發器Trigger、調度器Scheduler。java
其中job是在觸發器Trigger觸發以後,經調度器Scheduler分配以後執行的任務。算法
預約義的job接口中,只有execute一個方法。設計模式
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public interface Job {
void execute(JobExecutionContext var1) throws JobExecutionException;
}
複製代碼
在抽象類中定義一個操做中的算法骨架,而且把一些步驟延遲到子類。bash
模板方法使得子類能夠不改變一個算法的結構便可從新定義該算法的某些特定步驟。ide
//抽象類
public abstract class AbstractClass {
public abstract void primitiveOperation1();
public abstract void primitiveOperation2();
// 模板方法,給出了邏輯的骨架
// 而邏輯的組成是一些相應的抽象操做,他們都推遲到子類實現
public void templateMethod() {
primitiveOperation1();
primitiveOperation2();
System.out.println("");
}
}
//具體實現類
```java
public class ConcreteClassA extends AbstractClass {
@Override
public void primitiveOperation1() {
System.out.println("具體類A方法1實現");
}
@Override
public void primitiveOperation2() {
System.out.println("具體類A方法2實現");
}
}
複製代碼
業務場景:
在實際生產中,不一樣的功能性的定時任務是寫在不一樣的Job中,他們經過實現Job接口,而且重寫execute方法實現不一樣定時任務的邏輯。函數
public class QuartzHelloJob implements Job {
private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(format.format(new Date()) + " Hello Quartz!");
}
}
複製代碼
可是大部分狀況下,每一個定時任務都有一段公共代碼,如觸發處理jobDetail的邏輯等,因此這裏定義一個抽象Job類,即BaseJob類來屏蔽execute方法,而且直接在execute方法中調用一個新的doJob()方法。這樣的話
1.可以提供一些通用的功能邏輯寫再execute()方法中,方便擴展
2.屏蔽了 JobExecutionContext 與 JobExecutionException學習
本設計是主要運用到了類的繼承,在Job接口中定義execute方法,而後用抽象類BaseJob去繼承,最後讓實現類XXXJob去執行具體的邏輯。子類重寫了父類的方法,若是子類調用該方法,運行的是子類的方法,不會運行父類該方法。ui
public abstract class BaseJob implements Job {
private static final Logger logger = Logger.getLogger(BaseJob.class);
@Override
public final void execute(JobExecutionContext context) throws JobExecutionException {
try {
dojob(XXX);
} catch (Exception e) {
logger.error("執行 Job 出錯!", e);
}
}
public abstract void dojob(String XXX);
}
複製代碼
XXXJob類實現:this
public class testJob extends BaseJob {
@Override
public String doJob(String jobParameters) throws Exception {
// 各自Job的邏輯實現
}
}
複製代碼
參考:blog.csdn.net/GAMEloft9/a…
blog.csdn.net/GAMEloft9/a…編碼
1. 建立SchedulerFactory
2. 建立Scheduler
3. 建立JobDetail
4. 建立Trigger
5. 註冊到Scheduler:scheduler.scheduleJob(jobDetail, trigger)
6. 啓動Scheduler:scheduler.start()
public class RAMJobTest {
@Test
public void testExecute() throws SchedulerException, InterruptedException {
// 1.建立Scheduler的工廠
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
// 2.從工廠中獲取調度器實例
Scheduler scheduler = schedulerFactory.getScheduler();
// 3.建立JobDetail
JobDetail jobDetail = JobBuilder.newJob(RAMJob.class)
.withDescription("this is a ram job") //job的描述
.withIdentity("ramJob", "ramGroup") //job 的name和group
.build();
// 4.建立Trigger
Trigger trigger = TriggerBuilder.newTrigger().withDescription("")
.withIdentity("ramTrigger", "ramTriggerGroup")
.startAt(new Date()) // 默認當前時間啓動
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) // 兩秒執行一次
.build();
// 5.註冊任務和定時器
scheduler.scheduleJob(jobDetail, trigger);
// 6.啓動調度器
scheduler.start();
System.out.println("啓動時間 : " + new Date() + " " + Thread.currentThread().getName());
Thread.sleep(60000);
System.out.println("done");
}
}
做者:icameisaw
連接:https://www.jianshu.com/p/3f77224ad9d4
來源:簡書
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
複製代碼
SchedulerFactory是生產Scheduler實例的工廠類
import java.util.Collection;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
public interface SchedulerFactory {
Scheduler getScheduler() throws SchedulerException;
Scheduler getScheduler(String var1) throws SchedulerException;
Collection<Scheduler> getAllSchedulers() throws SchedulerException;
}
這個接口有兩個實現類:
StdSchedulerFacotory經過配置文件來設置Scheduler的各項參數
DirectSchedulerFactory主要經過硬編碼來設置Scheduler的各項參數
複製代碼
本項目使用的是StdSchedulerFacotory:
建立Scheduler實例的流程: www.jianshu.com/p/760a96048…JobDetailImpl是接口JobDetail的惟一實現類,本質上來講是一個Java Bean,這裏主要是要理解各個屬性的意思。
public class JobDetailImpl implements Cloneable, java.io.Serializable, JobDetail {
// Job的名稱
private String name;
// Job的分組
private String group = Scheduler.DEFAULT_GROUP;
// Job的描述
private String description;
// 執行Job業務邏輯的對應實體類的Class引用
private Class<? extends Job> jobClass;
// 保存關於Job的信息,根據業務邏輯本身放進去
private JobDataMap jobDataMap;
// 當沒有綁定Trigger的狀況,是否保存Job
private boolean durability = false;
// Job是否可從「恢復」狀況下再次執行
private boolean shouldRecover = false;
// 封裝了name和group,做爲JobDetail的惟一標識
// 用空間來換取時間和可讀性的策略
private transient JobKey key = null;
}
複製代碼
JobDataMap提供了一個Map<String, Object>的對象,咱們能夠在管理或者執行Job的過程當中保存或者查詢一些自定義的信息。
// 建立JobDetail
JobDetail jobDetail = JobBuilder.newJob(RAMJob.class)
.withDescription("this is a ram job") //job的描述
.withIdentity("ramJob", "ramGroup") //job 的name和group
.build();
// build方法
public JobDetail build() {
JobDetailImpl job = new JobDetailImpl();
job.setJobClass(jobClass);
job.setDescription(description);
if(key == null)
key = new JobKey(Key.createUniqueName(null), null);
job.setKey(key);
job.setDurability(durability);
job.setRequestsRecovery(shouldRecover);
if(!jobDataMap.isEmpty())
job.setJobDataMap(jobDataMap);
return job;
}
//jobDataMap字段屬性
public class JobBuilder {
public JobBuilder withIdentity(String name) public JobBuilder withIdentity(String name, String group) public JobBuilder withIdentity(JobKey jobKey) public JobBuilder withDescription(String jobDescription) public JobBuilder ofType(Class<? extends Job> jobClazz) public JobBuilder requestRecovery() public JobBuilder requestRecovery(boolean jobShouldRecover) public JobBuilder storeDurably() public JobBuilder storeDurably(boolean jobDurability) public JobBuilder usingJobData(String dataKey, String value) public JobBuilder usingJobData(String dataKey, Integer value) public JobBuilder usingJobData(String dataKey, Long value) public JobBuilder usingJobData(String dataKey, Float value) public JobBuilder usingJobData(String dataKey, Double value) public JobBuilder usingJobData(String dataKey, Boolean value) public JobBuilder usingJobData(JobDataMap newJobDataMap) public JobBuilder setJobData(JobDataMap newJobDataMap) } 複製代碼
Trigger trigger = TriggerBuilder.newTrigger().withDescription("")
.withIdentity("ramTrigger", "ramTriggerGroup")
.startAt(new Date()) // 默認當前時間啓動
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) // 兩秒執行一次
.usingJobData("triggerKey", "some important information to save")
.build();
複製代碼
TriggerBuilder是一個泛型類,與JobBuilder有點不同,並且建立Trigger實例的動做委託給了ScheduleBuilder類。ScheduleBuilder,此次不顧名思義了,它做爲一個生成器,不是要生成Scheduler類,而是要生成MutableTrigger實例。
scheduler.scheduleJob(jobDetail, trigger)
複製代碼
流程以下:
StdScheduler的方法基本上都代理給QuartzScheduler類來處理
org.quartz.Scheduler接口的間接實現,quartz的核心調度類,任務的調度和任務的管理都是QuartzScheduler實現的,而後經過一個靜態代理類StdScheduler提供出來。
public class QuartzScheduler implements RemotableQuartzScheduler {
// QuartzSchedulerResources對象是經過構造器放進去的
public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval) throws SchedulerException {
this.resources = resources;
if (resources.getJobStore() instanceof JobListener) {
addInternalJobListener((JobListener)resources.getJobStore());
}
this.schedThread = new QuartzSchedulerThread(this, resources);
ThreadExecutor schedThreadExecutor = resources.getThreadExecutor();
schedThreadExecutor.execute(this.schedThread);
if (idleWaitTime > 0) {
this.schedThread.setIdleWaitTime(idleWaitTime);
}
jobMgr = new ExecutingJobsManager();
addInternalJobListener(jobMgr);
errLogger = new ErrorLogger();
addInternalSchedulerListener(errLogger);
signaler = new SchedulerSignalerImpl(this, this.schedThread);
getLog().info("Quartz Scheduler v." + getVersion() + " created.");
}
public Date scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
validateState();
if (jobDetail == null) {
throw new SchedulerException("JobDetail cannot be null");
}
if (trigger == null) {
throw new SchedulerException("Trigger cannot be null");
}
if (jobDetail.getKey() == null) {
throw new SchedulerException("Job's key cannot be null");
}
if (jobDetail.getJobClass() == null) {
throw new SchedulerException("Job's class cannot be null");
}
// TriggerBuilder.build()會生成一個OperableTrigger實例。
OperableTrigger trig = (OperableTrigger)trigger;
if (trigger.getJobKey() == null) {
trig.setJobKey(jobDetail.getKey());
} else if (!trigger.getJobKey().equals(jobDetail.getKey())) {
throw new SchedulerException(
"Trigger does not reference given job!");
}
trig.validate();
Calendar cal = null;
if (trigger.getCalendarName() != null) {
cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());
}
// TODO: 解析各類類型的Trigger
Date ft = trig.computeFirstFireTime(cal);
if (ft == null) {
throw new SchedulerException(
"Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
}
// 關鍵代碼就是下面這一行
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
notifySchedulerListenersJobAdded(jobDetail);
notifySchedulerThread(trigger.getNextFireTime().getTime());
notifySchedulerListenersSchduled(trigger);
return ft;
}
// 其餘代碼
}
複製代碼
public class QuartzScheduler implements RemotableQuartzScheduler {
public void start() throws SchedulerException {
if (shuttingDown|| closed) {
throw new SchedulerException(
"The Scheduler cannot be restarted after shutdown() has been called.");
}
// QTZ-212 : calling new schedulerStarting() method on the listeners
// right after entering start()
notifySchedulerListenersStarting();
if (initialStart == null) {//初始化標識爲null,進行初始化操做
initialStart = new Date();
// RAMJobStore 啥都不作
// JobStoreSupport 判斷是否集羣,恢復Job等
this.resources.getJobStore().schedulerStarted();
startPlugins();
} else {
resources.getJobStore().schedulerResumed();// 若是已經初始化過,則恢復jobStore
}
schedThread.togglePause(false);// 喚醒全部等待的線程
getLog().info("Scheduler " + resources.getUniqueIdentifier() + " started.");
notifySchedulerListenersStarted();
}
// 其餘代碼
}
複製代碼
public class QuartzSchedulerThread extends Thread {
/**
* pause爲true,發出讓主循環暫停的信號,以便線程在下一個可處理的時刻暫停
* pause爲false,喚醒sigLock對象的全部等待隊列的線程
*/
void togglePause(boolean pause) {
synchronized (sigLock) {
paused = pause;
if (paused) {
signalSchedulingChange(0);
} else {
sigLock.notifyAll();
}
}
}
// 其餘代碼
}
複製代碼
Listener事件監聽是觀察者模式的一個應用。
QuartzScheduler的scheduleJob()start()方法都有notifyXXX代碼邏輯,這些就是JobDetail、Trigger和Scheduler事件監聽的代碼邏輯。
在《Scheduler的初始化》篇章裏面,初始化一個Scheduler,裏面有"根據PropertiesParser建立Listeners"的步驟,**Listeners就包括JobListener和TriggerListener的List對象**。
SchedulerListener不支持配置在quartz.properties裏面,初始化Scheduler的過程當中沒有這一塊的代碼邏輯。若是要添加一個觀察者,那麼能夠**經過StdScheduler.getListenerManager()獲取ListenerManager實例**,經過它能夠拿到全部觀察者的引用。
複製代碼
Subject通知Observer,都是遍歷Observer列表,觸發相應的通知,實現事件監聽的效果。
qs = new QuartzScheduler(rsrcs, idleWaitTime, dbFailureRetry);
複製代碼
// QuartzSchedulerThread建立和啓動的邏輯
this.schedThread = new QuartzSchedulerThread(this, resources);
ThreadExecutor schedThreadExecutor = resources.getThreadExecutor();
schedThreadExecutor.execute(this.schedThread);
複製代碼
public class DefaultThreadExecutor implements ThreadExecutor {
public void initialize() {
}
public void execute(Thread thread) {
thread.start();
}
}
複製代碼
STATE_BLOCKED 4 阻塞 STATE_COMPLETE 2 完成 STATE_ERROR 3 錯誤 STATE_NONE -1 不存在 STATE_NORMAL 0 正常 STATE_PAUSED 1 暫停
Scheduler scheduler = schedulerFactoryBean.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
TriggerState state = scheduler.getTriggerState(triggerKey);
複製代碼
設計模式的學習最好能結合業務場景同時理解。
@Component這個註解的做用標記這個類能夠被IOC容器管理,類上加上此標記可以讓Spring掃描到。