热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

精进Quartz源码—Quartz中JobStore保存JonDetail和Trigger源码分析(一)

我都是分析的jobStore方式为jdbc的SimpleTrigger!RAM的方式类似分析方式!要使用定时器,并讲任务持久到数据库,我们一定明白JobDetail和Trigger

=================================================

对人工智能感兴趣的伙伴,分享一个我朋友的人工智能教程。零基础!通俗易懂!风趣幽默!大家可以看看是否对自己有帮助,点击这里查看教程。

=================================================

欢迎关注我的公众号: Java编程技术乐园。分享技术,一起精进Quartz!

做一个积极的人

编码、改bug、提升自己

我有一个乐园,面向编程,春暖花开!

我都是分析的jobStore 方式为jdbc的SimpleTrigger!RAM的方式类似分析方式!

说明:


  • {0} :表的前缀 ,如表qrtz_trigger ,{0}== qrtz_


  • {1}:quartz.properties 中配置的 org.quartz.scheduler.instanceName: myInstanceName ,{1} ==myInstanceName


如果要使用定时器,那么正常企业级开发中一定是要将任务持久到数据库,那么我们一定明白JobDetail和Trigger是如何操作进入数据库,如何注册到Scheduler中!

一、从一个小Demo开始

那么下面就开始分析源码,分析源码前首先加一个小的demo,方面解释!

//1.创建Scheduler的工厂
SchedulerFactory sf = new StdSchedulerFactory();
//2.从工厂中获取调度器实例
Scheduler scheduler = sf.getScheduler();
//3.创建JobDetail
JobDetail jb = JobBuilder.newJob(RAMJob.class)
.withDescription("this is a ram job") //job的描述
.withIdentity("ramJob", "ramGroup") //job 的name和group
.build();
//任务运行的时间,SimpleSchedle类型触发器有效
long time= System.currentTimeMillis() + 3*1000L; //3秒后启动任务
Date statTime = new Date(time);
//4.创建Trigger
//使用SimpleScheduleBuilder或者CronScheduleBuilder
Trigger t = TriggerBuilder.newTrigger()
.withDescription("")
.withIdentity("ramTrigger", "ramTriggerGroup")
//.withSchedule(SimpleScheduleBuilder.simpleSchedule())
.startAt(statTime) //默认当前时间启动
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) //两秒执行一次
.build();
//5.注册任务和定时器
scheduler.scheduleJob(jb, t);//源码分析
//6.启动 调度器
scheduler.start();



二、开始源码分析

主要分析入口 scheduleJob ,从这里进去,看看源码里面到底是怎么进行处理的!

//5.注册任务和定时器
scheduler.scheduleJob(jb, t);//源码分析

2.1、scheduler.scheduleJob(jb, t);

开始一个任务调度 。scheduler.scheduleJob(jb, t);//源码分析

/** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. * 调用“代理”上的等效方法。 * * 实现 在 StdScheduler.scheduleJob(JobDetail jobDetail, Trigger trigger) *

*/
public Date scheduleJob(JobDetail jobDetail, Trigger trigger)
throws SchedulerException {

//这里的sched 是 QuartzScheduler 对象,Quartz和核心类,Quartz调度器
return sched.scheduleJob(jobDetail, trigger);//保存JobDetail和Trigger
}

2.2、ched.scheduleJob(jobDetail, trigger);

保存JobDetail和Trigger。ched.scheduleJob(jobDetail, trigger);//保存JobDetail和Trigger

/** *

* Add the {@link org.quartz.Job} identified by the given * {@link org.quartz.JobDetail} to the Scheduler, and * associate the given {@link org.quartz.Trigger} with it. *

* *

* If the given Trigger does not reference any Job, then it * will be set to reference the Job passed with it into this method. *

* * @throws SchedulerException * if the Job or Trigger cannot be added to the Scheduler, or * there is an internal Scheduler error. * 将给定org.quartz.JobDetail标识的org.quartz.Job添加到Scheduler, * 并将给定的org.quartz.Trigger与其关联。 * 如果给定的触发器不引用任何作业,则它将被设置为引用与其一起传递的作业到此方法中。 * * 实现在 QuartzScheduler.scheduleJob(JobDetail jobDetail, * Trigger trigger) */
public Date scheduleJob(JobDetail jobDetail,
Trigger trigger) throws SchedulerException {

validateState();//验证调度器是否关闭,关闭抛出异常
//检查 jobDetail和trigger
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");
}
OperableTrigger trig = (OperableTrigger)trigger;
//getJobKey 获取 getJobName(), getJobGroup()
if (trigger.getJobKey() == null) {

trig.setJobKey(jobDetail.getKey());
} else if (!trigger.getJobKey().equals(jobDetail.getKey())) {

throw new SchedulerException(
"Trigger does not reference given job!");
}
//验证trigger
trig.validate();
Calendar cal = null;
if (trigger.getCalendarName() != null) {

cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());//检索Calendar
"SELECT * FROM {0}CALENDARS WHERE SCHED_NAME = {1} AND CALENDAR_NAME = ?"
}
//在触发器首次添加到调度程序时由调度程序调用,以便让触发器基于任何关联的日历计算
//其第一次触发时间。调用此方法后,getNextFireTime()应返回有效的答案。
Date ft = trig.computeFirstFireTime(cal);
if (ft == null) {

throw new SchedulerException(
"Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
}
//存储给定的org.quartz.JobDetail和org.quartz.Trigger。
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);// 主要看这一行
notifySchedulerListenersJobAdded(jobDetail);
notifySchedulerThread(trigger.getNextFireTime().getTime());
notifySchedulerListenersSchduled(trigger);
return ft;
}

2.3、 resources.getJobStore().storeJobAndTrigger(jobDetail, trig);

存储给定的org.quartz.JobDetail和org.quartz.Trigger。 resources.getJobStore().storeJobAndTrigger(jobDetail, trig);

public void storeJobAndTrigger(final JobDetail newJob,
final OperableTrigger newTrigger)
throws JobPersistenceException {

executeInLock(
(isLockOnInsert()) ? LOCK_TRIGGER_ACCESS : null,
new VoidTransactionCallback() {

public void executeVoid(Connection conn) throws JobPersistenceException {

//(1)保存JobDetail
storeJob(conn, newJob, false);
//(2)保存Trigger
storeTrigger(conn, newTrigger, newJob, false,
Constants.STATE_WAITING, false, false);
}
});
}

2.3.1 storeJob(conn, newJob, false);

保存JobDetail 。storeJob(conn, newJob, false);

首先判断JobDetail是否已经存在,存在则更新,否则插入!

protected void storeJob(Connection conn,
JobDetail newJob, boolean replaceExisting)
throws JobPersistenceException {

//判断JobDetail是否已经存在,根据name和group
boolean existingJob = jobExists(conn, newJob.getKey());
try {

if (existingJob) {

if (!replaceExisting) {

throw new ObjectAlreadyExistsException(newJob);
}
//更新JobDetail
getDelegate().updateJobDetail(conn, newJob);
"UPDATE {0}JOB_DETAILS SET DESCRIPTION = ?, JOB_CLASS_NAME = ?, IS_DURABLE = ?, IS_NOnCONCURRENT= ?, IS_UPDATE_DATA = ?, REQUESTS_RECOVERY = ?, JOB_DATA = ? WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
} else {

//插入JobDetail
getDelegate().insertJobDetail(conn, newJob);
"INSERT INTO {0}JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP, DESCRIPTION, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA) VALUES({1}, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
}
} catch (IOException e) {

throw new JobPersistenceException("Couldn't store job: "
+ e.getMessage(), e);
} catch (SQLException e) {

throw new JobPersistenceException("Couldn't store job: "
+ e.getMessage(), e);
}
}

2.3.2 storeTrigger(conn, newTrigger, newJob, false,Constants.STATE_WAITING, false, false);

保存Trigger。 storeTrigger(conn, newTrigger, newJob, false,Constants.STATE_WAITING, false, false);

protected void storeTrigger(Connection conn,
OperableTrigger newTrigger, JobDetail job, boolean replaceExisting, String state,
boolean forceState, boolean recovering)
throws JobPersistenceException {

//判断Trigger是否已经存在,根据name和group
boolean existingTrigger = triggerExists(conn, newTrigger.getKey());
if ((existingTrigger) && (!replaceExisting)) {

throw new ObjectAlreadyExistsException(newTrigger);
}
try {

boolean shouldBepaused;
//进行一些状态的判断
if (!forceState) {

shouldBepaused = getDelegate().isTriggerGroupPaused(
conn, newTrigger.getKey().getGroup());
if(!shouldBepaused) {

shouldBepaused = getDelegate().isTriggerGroupPaused(conn,
ALL_GROUPS_PAUSED);
if (shouldBepaused) {

getDelegate().insertPausedTriggerGroup(conn, newTrigger.getKey().getGroup());
}
}
if (shouldBepaused && (state.equals(STATE_WAITING) || state.equals(STATE_ACQUIRED))) {

state = STATE_PAUSED;
}
}
//若job为null,重新获取!
if(job == null) {

job = getDelegate().selectJobDetail(conn, newTrigger.getJobKey(), getClassLoadHelper());
"SELECT * FROM {0}JOB_DETAILS WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
}
if (job == null) {

throw new JobPersistenceException("The job ("
+ newTrigger.getJobKey()
+ ") referenced by the trigger does not exist.");
}
//判断是否有DisallowConcurrentExecution注解,recovering恢复
if (job.isConcurrentExectionDisallowed() && !recovering) {

state = checkBlockedState(conn, job.getKey(), state);
"if (jobName != null) {

ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGERS_OF_JOB));
"SELECT * FROM {0}FIRED_TRIGGERS WHERE SCHED_NAME = {1} AND JOB_NAME = ? AND JOB_GROUP = ?"
ps.setString(1, jobName);
ps.setString(2, groupName);
} else {

ps = conn
.prepareStatement(rtp(SELECT_FIRED_TRIGGERS_OF_JOB_GROUP));
"SELECT * FROM {0}FIRED_TRIGGERS WHERE SCHED_NAME = {1} AND JOB_GROUP = ?"
ps.setString(1, groupName);
}"
}
if (existingTrigger) {

//更新trigger
getDelegate().updateTrigger(conn, newTrigger, state, job);
" 大概贴出一些,具体想看完整的进入源码看
if(updateJobData) {

ps = conn.prepareStatement(rtp(UPDATE_TRIGGER));
"UPDATE {0}TRIGGERS SET JOB_NAME = ?, JOB_GROUP = ?, DESCRIPTION = ?, NEXT_FIRE_TIME = ?, PREV_FIRE_TIME = ?, TRIGGER_STATE = ?, TRIGGER_TYPE = ?, START_TIME = ?, END_TIME = ?, CALENDAR_NAME = ?, MISFIRE_INSTR = ?, PRIORITY = ?, JOB_DATA = ? WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?"
} else {

ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_SKIP_DATA));
"UPDATE {0}TRIGGERS SET JOB_NAME = ?, JOB_GROUP = ?, DESCRIPTION = ?, NEXT_FIRE_TIME = ?, PREV_FIRE_TIME = ?, TRIGGER_STATE = ?, TRIGGER_TYPE = ?, START_TIME = ?, END_TIME = ?, CALENDAR_NAME = ?, MISFIRE_INSTR = ?, PRIORITY = ? WHERE SCHED_NAME = {1} AND TRIGGER_NAME = ? AND TRIGGER_GROUP = ?"
}
"
} else {

//插入trigger
getDelegate().insertTrigger(conn, newTrigger, state, job);
"INSERT INTO {0}TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, JOB_NAME, JOB_GROUP, DESCRIPTION, NEXT_FIRE_TIME, PREV_FIRE_TIME, TRIGGER_STATE, TRIGGER_TYPE, START_TIME, END_TIME, CALENDAR_NAME, MISFIRE_INSTR, JOB_DATA, PRIORITY) VALUES({1}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
}
} catch (Exception e) {

throw new JobPersistenceException("Couldn't store trigger '" + newTrigger.getKey() + "' for '"
+ newTrigger.getJobKey() + "' job:" + e.getMessage(), e);
}
}

到这里,保存就结束了!将任务保存成功后,就可以启动了,期待下一篇源码的分析:Scheduler的start()和QuartzSchedulerThread的run()!

三、参考资料

quartz2.2源码分析4-JobStore

Quartz专栏系列

1.精进Quartz——Quartz大致介绍(一)
2.精进Quartz——Quartz简单入门Demo(二)
3.精进Quartz——Spring和Quartz集成详解
4.精进Quartz——SSMM(Spring+SpringMVC+Mybatis+Mysql)和Quartz集成详解(四)
5.精进Quartz源码——JobStore保存JonDetail和Trigger源码分析(一)
6.精进Quartz源码——scheduler.start()启动源码分析(二)
7.精进Quartz源码——QuartzSchedulerThread.run() 源码分析(三)
8.精进Quartz源码——Quartz调度器的Misfire处理规则(四)


谢谢你的阅读,如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到!祝你每天开心愉快!


不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页 : http://blog.csdn.net/u010648555

愿你我在人生的路上能都变成最好的自己,能够成为一个独挡一面的人
精进Quartz源码—Quartz中JobStore保存JonDetail和Trigger源码分析(一)

© 每天都在变得更好的阿飞云


推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
author-avatar
爱-雨轩_627
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有