在调度器的运行篇章中,可以看到在QuartzSchedulerThread的线程中,会从数据库竞争trigger,竞争到trigger后,执行trigger所绑定的job Job的运行是在SimpleThreadPool的线程中运行,回顾一下QuartzSchedulerThread.tun()
public void run() {
....
int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
// 当SimpleThreadPool中有可用线程的时候,才会进入往下走
if(availThreadCount > 0) {
....
for (int i = 0; i < bndles.size(); i++) {
TriggerFiredResult result = bndles.get(i);
TriggerFiredBundle bndle = result.getTriggerFiredBundle();
....
JobRunShell shell = null;
try {
// 把trigger和job的信息封装在JobRunShell里面
shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
shell.initialize(qs);
} catch (SchedulerException se) {
....
}
// 由于JobRunShell实现了Runnable接口,所以可以将其放入线程池中跑
if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
getLog().error("ThreadPool.runInThread() return false!");
qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR);
}
}
....
}
QuartzSchedulerThread.tun()方法中,先从数据库取出待运行的trigger和job的信息,储存在TriggerFiredBundle类中
由下面第二个类图可见,jobDetail类型的job字段对应job的信息,OperableTrigger类型的trigger字段对应trigger的信息
代码中从TriggerFiredResult类取出TriggerFiredBundle类型的triggerFiredBundle字段对象来初始化JobRunShell
JobRunShellFactory的实现类是JTAAnnotationAwareJobRunShellFactory,JTAAnnotationAwareJobRunShellFactory.createJobRunShell()方法代码如下
public JobRunShell createJobRunShell(TriggerFiredBundle bundle) throws SchedulerException {
// 获取job实现类的ExecuteInJTATransaction注解
// JTA事务是一种不同于JDBC事务的事务类型
ExecuteInJTATransaction jtaAnnotation = ClassUtils.getAnnotation(bundle.getJobDetail().getJobClass(), ExecuteInJTATransaction.class);
if(jtaAnnotation == null)
// 一般使用JDBC事务,也就是进入这个分支,初始化一个JobRunShell并返回
return new JobRunShell(scheduler, bundle);
else {
int timeout = jtaAnnotation.timeout();
if (timeout >= 0) {
return new JTAJobRunShell(scheduler, bundle, timeout);
} else {
return new JTAJobRunShell(scheduler, bundle);
}
}
}
然后调用JobRunShell.initialize(qs)方法,初始化待运行的Job
public void initialize(QuartzScheduler sched)
throws SchedulerException {
this.qs = sched;
Job job = null;
JobDetail jobDetail = firedTriggerBundle.getJobDetail();
try {
// 这里调用QuartzScheduler的jobFactory的newJob()方法
// 默认值(private JobFactory jobFactory = new PropertySettingJobFactory())
job = sched.getJobFactory().newJob(firedTriggerBundle, scheduler);
} catch (SchedulerException se) {
....
} catch (Throwable ncdfe) {
....
}
// 这里初始化JobExecutionContextImpl对象jec,传入scheduler,trigger和job信息
// 注意这个对象可以被实际执行定时任务的Job拿到
this.jec = new JobExecutionContextImpl(scheduler, firedTriggerBundle, job);
}
PropertySettingJobFactory.newJob()方法其实就是调用反射来实例化一个JobClass的对象
public Job newJob(TriggerFiredBundle bundle, Scheduler Scheduler) throws SchedulerException {
JobDetail jobDetail = bundle.getJobDetail();
Class<? extends Job> jobClass = jobDetail.getJobClass();
try {
....
return jobClass.newInstance();
} catch (Exception e) {
....
}
}
初始化JobRunShell对象后,就是把这个对象丢入SimpleThreadPool中执行
public void run() {
qs.addInternalSchedulerListener(this);
try {
OperableTrigger trigger = (OperableTrigger) jec.getTrigger();
JobDetail jobDetail = jec.getJobDetail();
do {
Job job = jec.getJobInstance();
try {
begin();
} catch (SchedulerException se) {
....
}
....
try {
// 所有定时任务要求实现Job接口,是因为实际执行的是Job.execute()方法
job.execute(jec);
} catch (JobExecutionException jee) {
....
} catch (Throwable e) {
....
}
// 执行完毕后更新数据库的trigger状态,删除qrtz_fired_triggers表的数据
....
qs.notifyJobStoreJobComplete(trigger, jobDetail, instCode);
// 正常情况下是break跳出循环了,只有的需要重新执行的时候,走不到break
break;
} while (true);
} finally {
qs.removeInternalSchedulerListener(this);
}
}
我简单写了一个定时任务,可以看到execute方法可以拿到JobExecutionContext对象
JobExecutionContext对象持有了trigger、job和scheduler的详细信息