任务调度框架 ElasticJob 是基于 Quartz 开发的,看它的源码时,发现它封装的 LiteJob
类包含了两个成员变量,但是调试任务调度的过程中,这两个成员变量是有数据的。
它们究竟是什么时候被赋值的呢?
以往我用 Quartz 的时候,如果自定义的 Job
类中包含成员变量的话,都是在无参构造函数中初始化的。一直以为,Job
的实现类都不适合包含成员变量呢!
ElasticJob 的调度流程是从 JobScheduler
类的 init
方法开始的,由它回归到了 Quartz 的任务创建过程,底层是用 API 创建了一个 LiteJob
任务类。
public void init() {
LiteJobConfiguration liteJobConfigFromRegCenter = schedulerFacade.updateJobConfiguration(liteJobConfig);
JobRegistry.getInstance().setCurrentShardingTotalCount(liteJobConfigFromRegCenter.getJobName(), liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getShardingTotalCount());
JobScheduleController jobScheduleController = new JobScheduleController(
createScheduler(), createJobDetail(liteJobConfigFromRegCenter.getTypeConfig().getJobClass()), liteJobConfigFromRegCenter.getJobName());
JobRegistry.getInstance().registerJob(liteJobConfigFromRegCenter.getJobName(), jobScheduleController, regCenter);
schedulerFacade.registerStartUpInfo(!liteJobConfigFromRegCenter.isDisabled());
jobScheduleController.scheduleJob(liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getCron());
}
这里的 createJobDetail
方法,它是架起 ElasticJob 和 Quartz 的桥梁。
private JobDetail createJobDetail(final String jobClass) {
JobDetail result = JobBuilder.newJob(LiteJob.class).withIdentity(liteJobConfig.getJobName()).build();
result.getJobDataMap().put(JOB_FACADE_DATA_MAP_KEY, jobFacade);
Optional<ElasticJob> elasticJobInstance = createElasticJobInstance();
if (elasticJobInstance.isPresent()) {
result.getJobDataMap().put(ELASTIC_JOB_DATA_MAP_KEY, elasticJobInstance.get());
} else if (!jobClass.equals(ScriptJob.class.getCanonicalName())) {
try {
result.getJobDataMap().put(ELASTIC_JOB_DATA_MAP_KEY, Class.forName(jobClass).newInstance());
} catch (final ReflectiveOperationException ex) {
throw new JobConfigurationException("Elastic-Job: Job class '%s' can not initialize.", jobClass);
}
}
return result;
}
这段代码就是我们所熟悉的 Quartz 调度 API 了啦, LiteJob
的定义非常简单:
public final class LiteJob implements Job {
@Setter
private ElasticJob elasticJob;
@Setter
private JobFacade jobFacade;
@Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
JobExecutorFactory.getJobExecutor(elasticJob, jobFacade).execute();
}
}
它由两个成员变量,它们的赋值在 createJobDetail
方法中存储到 JobDataMap
中了,数据的 key 与变量名称一致。
接着到了 Quartz 执行任务调度时,创建实例的实例会自动完成成员变量的赋值操作。
PropertySettingJobFactory
创建 Job 实例时,会获取 JobDataMap
中的数据,通过反射设置实例的成员变量。
具体源码是:
@Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
Job job = super.newJob(bundle, scheduler);
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.putAll(scheduler.getContext());
jobDataMap.putAll(bundle.getJobDetail().getJobDataMap());
jobDataMap.putAll(bundle.getTrigger().getJobDataMap());
setBeanProps(job, jobDataMap);
return job;
}
setBeanProps
完成对 Job 实例类的属性设置,它会遍历 JobDataMap
中的所有数据,然后根据数据类型,和 key 找到对应的 setter
方法,去掉了细节判断的主要代码如下:
protected void setBeanProps(Object obj, JobDataMap data) throws SchedulerException {
for (Iterator<?> entryIter = data.getWrappedMap().entrySet().iterator(); entryIter.hasNext();) {
Map.Entry<?,?> entry = (Map.Entry<?,?>)entryIter.next();
String name = (String)entry.getKey();
String c = name.substring(0, 1).toUpperCase(Locale.US);
String methName = "set" + c + name.substring(1);
java.lang.reflect.Method setMeth = getSetMethod(methName, propDescs);
Class<?> paramType = null;
Object o = null;
try {
paramType = setMeth.getParameterTypes()[0];
o = entry.getValue();
Object parm = null;
if (paramType.isPrimitive()) {
if (paramType.equals(int.class)) {
if (o instanceof String) {
parm = Integer.valueOf((String)o);
} else if (o instanceof Integer) {
parm = o;
}
} else if (paramType.equals(long.class)) {
if (o instanceof String) {
parm = Long.valueOf((String)o);
} else if (o instanceof Long) {
parm = o;
}
} else if (paramType.equals(float.class)) {
if (o instanceof String) {
parm = Float.valueOf((String)o);
} else if (o instanceof Float) {
parm = o;
}
} else if (paramType.equals(double.class)) {
if (o instanceof String) {
parm = Double.valueOf((String)o);
} else if (o instanceof Double) {
parm = o;
}
} else if (paramType.equals(boolean.class)) {
if (o instanceof String) {
parm = Boolean.valueOf((String)o);
} else if (o instanceof Boolean) {
parm = o;
}
} else if (paramType.equals(byte.class)) {
if (o instanceof String) {
parm = Byte.valueOf((String)o);
} else if (o instanceof Byte) {
parm = o;
}
} else if (paramType.equals(short.class)) {
if (o instanceof String) {
parm = Short.valueOf((String)o);
} else if (o instanceof Short) {
parm = o;
}
} else if (paramType.equals(char.class)) {
if (o instanceof String) {
String str = (String)o;
if (str.length() == 1) {
parm = Character.valueOf(str.charAt(0));
}
} else if (o instanceof Character) {
parm = o;
}
}
} else if ((o != null) && (paramType.isAssignableFrom(o.getClass()))) {
parm = o;
}
setMeth.invoke(obj, new Object[]{ parm });
}
这就解释了为什么 LiteJob
类包含两个成员变量,但是实例还被正常创建且属性被正常赋值的问题。
通过跟踪 ElasticJob 源码,发现了自己对 Quartz 的知识盲点,也是一个神奇收获。
以后有机会再使用 Quartz 的时候,就又多了一种设置 Job 成员变量的途径了。LiteJob
这种优雅的类定义,还是可以学一下的!