浏览 10216 次
锁定老帖子 主题:[原创]sexy Quartz
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-11-28
于是便有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(真复杂) JobDetail jobDetail = new JobDetail("myJob", // job name sched.DEFAULT_GROUP, // job group DumbJob.class); // the java class to execute Trigger trigger = TriggerUtils.makeDailyTrigger(8, 30); trigger.setStartTime(new Date()); trigger.setName("myTrigger"); sched.scheduleJob(jobDetail, trigger); 首先!!我在这里要明确一个事情。Job类是没有状态的!! 这是什么概念呢,就是说,你实现的一个Job(例如上面的代码的DumbJob),并不是由你自己new出来的,留意一下new JobDetail的代码,传入的参数是DumbJob.class,而不是一个具体的job实例。Quartz帮你吧Job new一份出来,并且调用相应的接口,并没有别的功能。 这里会带来一个什么问题呢,我们先来看看Spring的辅助类。 Spring有两个辅助类可以产生JobDetail类,需要留意的是,Spring并不辅助产生Job类,也就是Spring认为Job类不需要管理。 我们先看看第一个,JobDetailBean <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="example.ExampleJob" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="5" /> </map> </property> </bean> 不知道大家有没有看出问题在哪里。property jobClass是一个类名,并不是一个实例名!也就是跟Quartz的调用一样,是Quartz负责帮你new一个example.ExampleJob类出来,也就是说你不能对Job类进行任何形式的注入(IOC),比如说,我们的example.ExampleJob是一个DAO,需要传入DataSource进行DB操作,没辙。 因此,Spring提供了另外一个JobDetail辅助类MethodInvokingJobDetailFactoryBean <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exampleBusinessObject" /> <property name="targetMethod" value="doIt" /> </bean> 你可以留意到,property targetObject是一个ref,指向的是一个常规的Spring管理的Bean。 但是! MethodInvokingJobDetailFactoryBean很不友好。首先,它是通过反射调用的,而不是Interface,因此我们必须要看了Spring的xml才能知道谁被调用了,你还可能会写一大堆property targetMethod=doIt,而且Job Interface是会传入一个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类 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类,并且调用它 接下来是戏玉了 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配置。 JobDetail jobDetail = new JobDetail(taskName + "Job", null, DummyJob.class); jobDetail.getJobDataMap() .put("methodInvoker", entry.getValue()); 这两句是关键 于是,Quartz变得更sexy了 ---------- 欢迎踩场 http://weavesky.com/2007/11/28/sexy-quartz 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-12-14
zgd 写道 ... 这里会带来一个什么问题呢,我们先来看看Spring的辅助类。 Spring有两个辅助类可以产生JobDetail类,需要留意的是,Spring并不辅助产生Job类,也就是Spring认为Job类不需要管理。 我们先看看第一个,JobDetailBean <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="example.ExampleJob" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="5" /> </map> </property> </bean> 不知道大家有没有看出问题在哪里。property jobClass是一个类名,并不是一个实例名!也就是跟Quartz的调用一样,是Quartz负责帮你new一个example.ExampleJob类出来,也就是说你不能对Job类进行任何形式的注入(IOC),比如说,我们的example.ExampleJob是一个DAO,需要传入DataSource进行DB操作,没辙。... 弱弱地问一句,可以像下面这样注入吗? <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="example.ExampleJob" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="5" /> <entry key="serviceFactory" value-ref="serviceFactory"/> </map> </property> </bean> <bean id="serviceFactory">...</bean> |
|
返回顶楼 | |
发表时间:2008-03-25
强强地答一句: 可以!
|
|
返回顶楼 | |
发表时间:2008-03-25
如果项目中使用,建议使用数据库的JOB+SP来实现数据的周期性管理。这个是目前最能保证稳定性的……
|
|
返回顶楼 | |
发表时间:2008-06-06
bluemeteor 写道 如果项目中使用,建议使用数据库的JOB+SP来实现数据的周期性管理。这个是目前最能保证稳定性的……
能不能介绍以下数据库JOB+SP怎么做?? |
|
返回顶楼 | |
发表时间:2008-06-06
不明白为什么做定时器一定要生成一个job.用这个job有什么用?
我的做法是1:spring集成的quartz.把业务bean和方法注入MethodInvokingJobDetailFactoryBean. 2:CronTriggerBean指定MethodInvokingJobDetailFactoryBean并且编写cronExpression. 3:SchedulerFactoryBean指定CronTriggerBean就OK了. 这样java代码不依赖任何jar.. 两种做法有什么区别? |
|
返回顶楼 | |