本章閱讀收獲:可了解Quartz框架中的Job部分源碼
源碼起航
人之初蝎抽,專門找軟柿子捏,所以我就又忍不住先拿Job進行開刀,作為一個敲門磚進行源碼分析樟结。
Quartz中的Job是什么养交?
聯(lián)系自己對于定時任務的理解,其實就是對于任務的抽象瓢宦,所以這個類其實你在不看源碼時碎连,可能就已經(jīng)就猜到了它是一個接口,一搜源碼驮履,果然沒錯:
package org.quartz;
/**
* 定時任務對于任務的抽象
*/
public interface Job {
/**
* 定時任務執(zhí)行邏輯
*/
void execute(JobExecutionContext context)
throws JobExecutionException;
}
非常簡單鱼辙,是否你再沒有看之前,自己也已經(jīng)構想到了呢玫镐?但是這邊又引發(fā)了另一個類不接倒戏,就是JobExecutionContext,但是細想一下恐似,你可能也會覺得在情理之中杜跷,因為在執(zhí)行任務中,可能你需要獲取任務信息矫夷。然后你可能會想葛闷,比如呢? 在生產(chǎn)環(huán)境中双藕,你可能會用到分布式的定時任務淑趾,那么你如何讓任務判斷自己改處理哪些數(shù)據(jù)信息呢,這個時候你可以獲取Job的上下文蔓彩,針對取余等等方式治笨,避免數(shù)據(jù)重復處理。
JobExecutionContext源碼解析
首先這也是一個接口赤嚼,由于內(nèi)部方法有點多旷赖,我們挑一個最重要的分析一下:
package org.quartz;
import java.util.Date;
/**
* Job任務執(zhí)行時獲取到的上下文
*/
public interface JobExecutionContext {
...
/**
* 獲取任務詳細信息
*/
public JobDetail getJobDetail();
...
}
對于這個類的實現(xiàn)類,默認是JobExecutionContextImpl更卒,由于篇幅原因等孵,這個就不做特地展開,因為這個類只是簡單的把屬性取出蹂空,很簡單俯萌。那接下來,便開始解決讀者的下一個以為上枕,什么是JobDetail咐熙?
JobDetail源碼解析
你可以先猜想一下,這個接口中會包含哪些信息辨萍?
我們直接進入它的實現(xiàn)類:
package org.quartz.impl;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.quartz.Trigger;
import org.quartz.utils.ClassUtils;
/**
* 任務相信信息
*/
public class JobDetailImpl implements Cloneable, java.io.Serializable, JobDetail {
private static final long serialVersionUID = -6069784757781506897L;
/**
* 任務名稱
*/
private String name;
/**
* 任務所屬組
*/
private String group = Scheduler.DEFAULT_GROUP;
/**
* 任務描述
*/
private String description;
/**
* 任務類
*/
private Class<? extends Job> jobClass;
/**
* 任務額外信息
*/
private JobDataMap jobDataMap;
private boolean durability = false;
private boolean shouldRecover = false;
/**
* 任務唯一標識
*/
private transient JobKey key = null;
public JobDetailImpl() {
}
public JobDetailImpl(String name, Class<? extends Job> jobClass) {
this(name, null, jobClass);
}
public JobDetailImpl(String name, String group, Class<? extends Job> jobClass) {
setName(name);
setGroup(group);
setJobClass(jobClass);
}
public JobDetailImpl(String name, String group, Class<? extends Job> jobClass,
boolean durability, boolean recover) {
setName(name);
setGroup(group);
setJobClass(jobClass);
setDurability(durability);
setRequestsRecovery(recover);
}
public String getName() {
return name;
}
public void setName(String name) {
if (name == null || name.trim().length() == 0) {
throw new IllegalArgumentException("Job name cannot be empty.");
}
this.name = name;
this.key = null;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
if (group != null && group.trim().length() == 0) {
throw new IllegalArgumentException(
"Group name cannot be empty.");
}
if (group == null) {
group = Scheduler.DEFAULT_GROUP;
}
this.group = group;
this.key = null;
}
public String getFullName() {
return group + "." + name;
}
public JobKey getKey() {
if(key == null) {
if(getName() == null)
return null;
key = new JobKey(getName(), getGroup());
}
return key;
}
public void setKey(JobKey key) {
if(key == null)
throw new IllegalArgumentException("Key cannot be null!");
setName(key.getName());
setGroup(key.getGroup());
this.key = key;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Class<? extends Job> getJobClass() {
return jobClass;
}
public void setJobClass(Class<? extends Job> jobClass) {
if (jobClass == null) {
throw new IllegalArgumentException("Job class cannot be null.");
}
if (!Job.class.isAssignableFrom(jobClass)) {
throw new IllegalArgumentException(
"Job class must implement the Job interface.");
}
this.jobClass = jobClass;
}
public JobDataMap getJobDataMap() {
if (jobDataMap == null) {
jobDataMap = new JobDataMap();
}
return jobDataMap;
}
public void setJobDataMap(JobDataMap jobDataMap) {
this.jobDataMap = jobDataMap;
}
...
@Override
public boolean equals(Object obj) {
if (!(obj instanceof JobDetail)) {
return false;
}
JobDetail other = (JobDetail) obj;
if(other.getKey() == null || getKey() == null)
return false;
if (!other.getKey().equals(getKey())) {
return false;
}
return true;
}
@Override
public int hashCode() {
JobKey key = getKey();
return key == null ? 0 : getKey().hashCode();
}
@Override
public Object clone() {
JobDetailImpl copy;
try {
copy = (JobDetailImpl) super.clone();
if (jobDataMap != null) {
copy.jobDataMap = (JobDataMap) jobDataMap.clone();
}
} catch (CloneNotSupportedException ex) {
throw new IncompatibleClassChangeError("Not Cloneable.");
}
return copy;
}
public JobBuilder getJobBuilder() {
JobBuilder b = JobBuilder.newJob()
.ofType(getJobClass())
.requestRecovery(requestsRecovery())
.storeDurably(isDurable())
.usingJobData(getJobDataMap())
.withDescription(getDescription())
.withIdentity(getKey());
return b;
}
}
相信大家對于這些屬性部分可以猜到棋恼,先講一個大家可能沒有太注意的Java關鍵字,是在屬性key上的transient,這個關鍵字是用來干什么的呢爪飘?是針對于序列化與反序列化時义起,不對這個字段進行序列化操作的。這么做是為什么呢师崎?我猜測是為了安全起見默终,以為定時任務是以這個key作為唯一標示的,序列化下把信息泄露可能會造成安全隱患犁罩。也可以看下這里重寫了hasCode和equals方法齐蔽,至于為什么這么做,大家應該能意會昼汗。這邊大家可能對于JobBuilder有些好奇肴熏?
JobBuilder源碼解析
JobBuilder是定時任務構造器,用于構造一個JobDetail顷窒,源碼如下:
package org.quartz;
import org.quartz.impl.JobDetailImpl;
import org.quartz.utils.Key;
/**
* 定時任務構造器,用于構造一個JobDetail
*/
public class JobBuilder {
/**
* 任務標識碼源哩,用于唯一確定任務
*/
private JobKey key;
private String description;
private Class<? extends Job> jobClass;
/**
* Durability鞋吉,持久性;如果Job是非持久性的励烦,一旦沒有Trigger與其相關聯(lián)谓着,
* 它就會從Scheduler中被刪除。也就是說Job的生命周期和其Trigger是關聯(lián)的坛掠。
*/
private boolean durability;
/**
* RequestsRecovery赊锚,如果為true,那么在Scheduler異常中止或者系統(tǒng)異常關閉后屉栓,當Scheduler重啟后舷蒲,Job會被重新執(zhí)行。
*/
private boolean shouldRecover;
private JobDataMap jobDataMap = new JobDataMap();
protected JobBuilder() {
}
public static JobBuilder newJob() {
return new JobBuilder();
}
public static JobBuilder newJob(Class <? extends Job> jobClass) {
JobBuilder b = new JobBuilder();
b.ofType(jobClass);
return b;
}
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;
}
public JobBuilder withIdentity(String name) {
key = new JobKey(name, null);
return this;
}
public JobBuilder withIdentity(String name, String group) {
key = new JobKey(name, group);
return this;
}
public JobBuilder withIdentity(JobKey jobKey) {
this.key = jobKey;
return this;
}
public JobBuilder withDescription(String jobDescription) {
this.description = jobDescription;
return this;
}
public JobBuilder ofType(Class <? extends Job> jobClazz) {
this.jobClass = jobClazz;
return this;
}
...
}
一些簡單的get友多、set方法被我刪除了牲平,可以看到這個類非常簡單,就是作為一個構造器域滥,根據(jù)屬性構造出一個JobDetail纵柿。
結束語
通過本章,我們可以逐步了解Job启绰、JobExecutionContext昂儒、 JobDetail、JobBuilder的源碼委可,可以大致掌握Job相關的源碼信息渊跋。大家也可以回想下,如果是你,你在架構定時框架時刹枉,任務這塊會如何架構叽唱,哪些地方你可以借鑒,哪些地方你可以做的更好~~~