quartz 中JobExecutionContext的使用 - garnettcwm的专栏 - CSDN diana garnett 是谁

一个系统里面经常需要做一些定时任务,比如说定时清空今日得分,或者定时清理临时文件。简单的定时任务很容易实现,用线程或者用Timer就可以了,但是始终需要自己写大量代码才能实现复杂的需求。

于是便有Quartz。不过,Quartz太久没有更新了,而且它太复杂。由于我的系统是基于Spring构建的,所以我希望能使用Spring支持的scheduling类库,可惜Spring只支持commonj和Quartz,正确来说,在Java界,并没有别的scheduling类库了,而commonj只是一个interface,没有具体的实现,似乎在Weblogic之类的里面有实现。

当然,也有另外一个选择,也是轻量级的脚本语言常用的做法,就是使用Linux的crontable,可以实现比较复杂的定时。不过,脚本语言调用数据库并不是很方便(应该说我们的团队技术累积上的问题),如果用crontable启动Java,每次启动的成本又比较高。

在评估过各种方案之后,我还是选择了使用Quartz,首先从Spring的辅助类开始入手吧。

题外话,在一个集群的环境里面(也就是多个Tomcat的环境下),定时任务应该是独立的应用,也就是不应该在每一个Tomcat里面都启动Quartz或者定时线程。另外,在Tomcat的应用里面,也是尽量不要使用线程,有可能一点点小错误就会导致整个Tomcat崩溃(其实我们还是使用很多的,呵呵)。

根据Quartz的使用行为,一个任务我们至少需要一个Job、一个JobDetail、一个Trigger(真复杂)

viewplaincopyto clipboardprint?

JobDetailjobDetail<span>=</span><span>new</span>JobDetail<span>(</span><span>"myJob"</span>,<span>//jobname</span>

sched.<span>DEFAULT_GROUP</span>,<span>//jobgroup</span>

DumbJob.<span>class</span><span>)</span><span>;</span><span>//thejavaclasstoexecute</span>

Triggertrigger<span>=</span>TriggerUtils.<span>makeDailyTrigger</span><span>(</span><span>8</span>,<span>30</span><span>)</span><span>;</span>

trigger.<span>setStartTime</span><span>(</span><span>new</span><span>Date</span><span>(</span><span>)</span><span>)</span><span>;</span>

trigger.<span>setName</span><span>(</span><span>"myTrigger"</span><span>)</span><span>;</span>

sched.<span>scheduleJob</span><span>(</span>jobDetail,trigger<span>)</span><span>;</span>

JobDetail jobDetail = new JobDetail("myJob", // job namesched.DEFAULT_GROUP, // job group DumbJob.class);// the java class to executeTrigger trigger = TriggerUtils.makeDailyTrigger(8, 30);trigger.setStartTime(new Date());trigger.setName("myTrigger");sched.scheduleJob(jobDetail, trigger);

首先!!我在这里要明确一个事情。Job类是没有状态的!!

这是什么概念呢,就是说,你实现的一个Job(例如上面的代码的DumbJob),并不是由你自己new出来的,留意一下newJobDetail的代码,传入的参数是DumbJob.class,而不是一个具体的job实例。Quartz帮你吧Jobnew一份出来,并且调用相应的接口,并没有别的功能。

这里会带来一个什么问题呢,我们先来看看Spring的辅助类。

Spring有两个辅助类可以产生JobDetail类,需要留意的是,Spring并不辅助产生Job类,也就是Spring认为Job类不需要管理。

我们先看看第一个,JobDetailBean

viewplaincopyto clipboardprint?

<span><span><bean</span><span>name</span>=<span>"exampleJob"</span><span>class</span>=<span>"org.springframework.scheduling.quartz.JobDetailBean"</span><span>></span></span>

<span><span><property</span><span>name</span>=<span>"jobClass"</span><span>value</span>=<span>"example.ExampleJob"</span><span>/></span></span>

<span><span><property</span><span>name</span>=<span>"jobDataAsMap"</span><span>></span></span>

<span><span><map<span>></span></span></span>

<span><span><entry</span><span>key</span>=<span>"timeout"</span><span>value</span>=<span>"5"</span><span>/></span></span>

<span><span></map<span>></span></span></span>

<span><span></property<span>></span></span></span>

<span><span></bean<span>></span></span></span>

<bean name="exampleJob"><property name="jobClass" value="example.ExampleJob" /><property name="jobDataAsMap"><map><entry key="timeout" value="5" /></map></property></bean>

不知道大家有没有看出问题在哪里。propertyjobClass是一个类名,并不是一个实例名!也就是跟Quartz的调用一样,是Quartz负责帮你new一个example.ExampleJob类出来,也就是说你不能对Job类进行任何形式的注入(IOC),比如说,我们的example.ExampleJob是一个DAO,需要传入DataSource进行DB操作,没辙。

因此,Spring提供了另外一个JobDetail辅助类MethodInvokingJobDetailFactoryBean

viewplaincopyto clipboardprint?

<span><span><bean</span><span>id</span>=<span>"jobDetail"</span><span>class</span>=<span>"org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"</span><span>></span></span>

<span><span><property</span><span>name</span>=<span>"targetObject"</span><span>ref</span>=<span>"exampleBusinessObject"</span><span>/></span></span>

<span><span><property</span><span>name</span>=<span>"targetMethod"</span><span>value</span>=<span>"doIt"</span><span>/></span></span>

<span><span></bean<span>></span></span></span>

<bean id="jobDetail"><property name="targetObject" ref="exampleBusinessObject" /><property name="targetMethod" value="doIt" /></bean>

你可以留意到,property targetObject是一个ref,指向的是一个常规的Spring管理的Bean。

但是!

MethodInvokingJobDetailFactoryBean很不友好。首先,它是通过反射调用的,而不是Interface,因此我们必须要看了Spring的xml才能知道谁被调用了,你还可能会写一大堆property targetMethod=doIt,而且JobInterface是会传入一个JobExecutionContext,这个被miss了。

其次,如果我们需要大量的Job的话(因为我就是做一个专门用来定时的应用),Spring的配置文件会变得非常臃肿,我希望Job和JobDetail不需要Spring专门管理,只要他是一个Spring管理的Bean,并且实现了Job这个接口就ok了。

这里补充一个事情,我们跳过了Trigger的部分,每一个JobDetail必须配备一个相应的Trigger,因此配置文件是你之前想象中的两倍那么大,而且你还得给每一个Bean命名一个ID,而这个类你以后都不会用到。

我的目标是:

1、只要是实现了Job接口的Spring管理的Bean,自动加入scheduling,根本不用关心JobDetail的存在,也不会有注入的问题

2、所有Job均使用CronTrigger,并且通过配置文件设定Cron Expressions

通过研究MethodInvokingJobDetailFactoryBean和Quartz的代码,我明白到JobDetail是有状态的,而MethodInvokingJobDetailFactoryBean正是利用这点来实现具体效果的,于是便有了我一下这些辅助代码

首先

,需要一个DummyJob,由于Quartz的主入口始终是Job类

viewplaincopyto clipboardprint?

<span>public</span><span>class</span>DummyJob<span>implements</span>Job<span>{</span>

span>public</span><span>void</span>execute<span>(</span>JobExecutionContextcontext<span>)</span>

<span>throws</span>JobExecutionException<span>{</span>

Jobjob<span>=</span><span>(</span>Job<span>)</span>context.<span>getMergedJobDataMap</span><span>(</span><span>)</span>.<span>get</span><span>(</span><span>"methodInvoker"</span><span>)</span><span>;</span>

<span>if</span><span>(</span>job<span>!=</span><span>null</span><span>)</span><span>{</span>

job.<span>execute</span><span>(</span>context<span>)</span><span>;</span>

<span>}</span>

span>}</span>

<span>}</span>

public class DummyJob implements Job {public void execute(JobExecutionContext context)throws JobExecutionException {Job job = (Job) context.getMergedJobDataMap().get("methodInvoker");if (job != null) {job.execute(context);}}}

jobDataMap就是JobDetail存储状态的地方,DummyJob唯一要做的就是,知道实际的Job类,并且调用它

接下来是戏玉了

viewplaincopyto clipboardprint?

Map<span><</span>String,Job<span>></span>jobMap<span>=</span>context.<span>getBeansOfType</span><span>(</span>Job.<span>class</span><span>)</span><span>;</span>

<span>for</span><span>(</span><span>Map</span>.<span>Entry</span><span><</span>String,Job<span>></span>entry<span>:</span>jobMap.<span>entrySet</span><span>(</span><span>)</span><span>)</span><span>{</span>

<span>String</span>taskName<span>=</span>entry.<span>getKey</span><span>(</span><span>)</span><span>;</span>

<span>String</span>cronExpression<span>=</span>props.<span>getProperty</span><span>(</span>taskName<span>)</span><span>;</span>

<span>if</span><span>(</span>cronExpression<span>==</span><span>null</span><span>)</span><span>{</span>

logger.<span>warn</span><span>(</span><span>"[{}]don'thaveacronExpression"</span>,taskName<span>)</span><span>;</span>

<span>continue</span><span>;</span>

<span>}</span>

<span>try</span><span>{</span>

Triggertrigger<span>=</span><span>new</span>CronTrigger<span>(</span>taskName<span>+</span><span>"Trigger"</span>,<span>null</span>,

cronExpression<span>)</span><span>;</span>

JobDetailjobDetail<span>=</span><span>new</span>JobDetail<span>(</span>taskName<span>+</span><span>"Job"</span>,<span>null</span>,

DummyJob.<span>class</span><span>)</span><span>;</span>

jobDetail.<span>getJobDataMap</span><span>(</span><span>)</span>

.<span>put</span><span>(</span><span>"methodInvoker"</span>,entry.<span>getValue</span><span>(</span><span>)</span><span>)</span><span>;</span>

scheduler.<span>scheduleJob</span><span>(</span>jobDetail,trigger<span>)</span><span>;</span>

<span>}</span><span>catch</span><span>(</span><span>ParseException</span>e<span>)</span><span>{</span>

logger.<span>error</span><span>(</span><span>""</span>,e<span>)</span><span>;</span>

<span>}</span><span>catch</span><span>(</span>SchedulerExceptione<span>)</span><span>{</span>

logger.<span>error</span><span>(</span><span>""</span>,e<span>)</span><span>;</span>

<span>}</span>

<span>}</span>

Map<String, Job> jobMap = context.getBeansOfType(Job.class);for (Map.Entry<String, Job> entry : jobMap.entrySet()) {String taskName = entry.getKey();String cronExpression = props.getProperty(taskName);if (cronExpression == null) {logger.warn("[{}] don't have a cronExpression", taskName);continue;}try {Trigger trigger = new CronTrigger(taskName + "Trigger", null,cronExpression);JobDetail jobDetail = new JobDetail(taskName + "Job", null,DummyJob.class);jobDetail.getJobDataMap().put("methodInvoker", entry.getValue());scheduler.scheduleJob(jobDetail, trigger);} catch (ParseException e) {logger.error("", e);} catch (SchedulerException e) {logger.error("", e);}}

从Spring context里面读取所有实现了Job的类遍历,props是从文件里面读取相应的cronExpression配置。

viewplaincopyto clipboardprint?

JobDetailjobDetail<span>=</span><span>new</span>JobDetail<span>(</span>taskName<span>+</span><span>"Job"</span>,<span>null</span>,

DummyJob.<span>class</span><span>)</span><span>;</span>

jobDetail.<span>getJobDataMap</span><span>(</span><span>)</span>

.<span>put</span><span>(</span><span>"methodInvoker"</span>,entry.<span>getValue</span><span>(</span><span>)</span><span>)</span><span>;</span>

JobDetail jobDetail = new JobDetail(taskName + "Job", null,DummyJob.class);jobDetail.getJobDataMap().put("methodInvoker", entry.getValue());

这两句是关键

于是,Quartz变得更sexy了

quartz 中JobExecutionContext的使用

假如execute方法中需要一些额外的数据怎么办?比如说execute

中希望发送一封邮件,但是我需要知道邮件的发送者、接收者等信息?

存在两种解决方案:

1.JobDataMap类:

每个JobDetail都关联了一个JobDataMap实例,JobDataMap是java.util.Map的子类,基本上是提供key-value形式的数据,并提供了一些便利方法(主要是对java基本数据类型的支持,如put(String key,intvalue)),当开发人员创建JobDetail的时候,可以把附加信息放到JobDataMap中,那么在execute方法中可以根据key找到需要的值。

JobDetail job = new JobDetail....

job.getJobDataMap().put("from","snowway@vip.sina.com");

...

在execute中

String from =jobExecutionContext.getJobDetail().getJobDataMap().getString("from");

....

不过,当你使用数据库存储JobDetail的时候(默认情况下使用RAM),这里有一个致命的弱点,你不能把没有实现java.io.Serializable的对象放入JobDataMap中,因为Quartz将使用Blob字段保存(也可以通过配置文件关闭)序列化过的JobDataMap中的对象。比如你在execute方法中需要一个java.sql.Connection接口实例,这种情况也是普遍的,那么通常情况下你不能把Connection放入JobDataMap,即使你只想在execute中使用。(注:读者可暂时认为上面这段话是正确的,然而可以通过指示quartz改变这种行为,那属于高级话题)

2.假如你需要一个java.sql.Connection,用于在execute中完成某些操作,那么你可以把Connection放入Quartz的SchedulerContext中,execute也可以访问,并且Quartz不会持久化SchedulerContext中的任何东西。

scheduler.getContext().put("java.sql.Connection",connection);

execute中

Connection con =(Connection)jobExecutionContext.getScheduler().getContext().get("java.sql.Connection");

  

爱华网本文地址 » http://www.413yy.cn/a/25101013/184725.html

更多阅读

厉以宁老师的诗词 厉以宁是谁的老师

今天接到大学班主任王其文老师的邮件,给我们发来了《厉以宁教授诞辰80周年暨从教55周年征文》修改后的新版本。读着这些文字,记忆的浪排排而来。我的那些恩师们和学长们、同学们的回忆真的是字字真切,感怀万千。等正式出版的时候,大家会

谁知道伍佰《突然的自我》女声版是谁唱的 突然的自我 伍佰mv

下午在虎跑路上,收音机传来一首歌,伍佰的《突然的自我》,有点沙哑的女声,有点像柯以敏。太动听了,不知道是谁唱的。突然的自我 伍佰那就不要留时光一过不再有你远眺的天空 挂更多的彩虹我会紧紧的 将你豪情放在心头在寒冬时候 就回忆

黄河故人的核武器系列 黄河故人是谁

saintfei:从茶馆和西西河网站收集整理的文章,感谢“黄河故人”贡献的一系列精彩文章,向为中国的核事业贡献了青春、事业、名望甚至生命的人致敬。你们是真正的民族英雄。第一章 中国周边国家核武器能力分析核武器应该不算武器,属于政治

CarlesRexach,决定梅西命运的人 性格决定命运是谁说的

当初他来巴萨试训最后决定收下他的是Carles Rexach(国家德比后梅西还问候了他)。CarlesRexach只是用了很短的时间就确定了巴萨需要签下梅西,他当时面临的是很多人的反对。反对声音中,一个最主要的就是他有生长发育

声明:《quartz 中JobExecutionContext的使用 - garnettcwm的专栏 - CSDN diana garnett 是谁》为网友倒计时分享!如侵犯到您的合法权益请联系我们删除