也许这篇文章的名字应该改成《Quartz JobFactory的使用》,因为正是使用JobFactory解决的Quartz Job类有参数构造方法的问题。同样,使用JobFactory也能解决Job已有实例重用的问题。
问题如标题描述:就是希望为Job类的实例传递参数,结果发现找到的文档中创建Job实例的方法多是通过类反射来实现的,这显然不能满足我的需求。
也找到了一些文章建议使用jobDataMap来解决。不太喜欢这种方案,因为太难看了——只是搬个砖而已,却连底裤都漏出来了。
翻了下官方文档,在 Lesson 12: Miscellaneous Features of Quartz 这一节找了关于 JobFactory 的描述,虽然寥寥几行,却指明了解决问题的方向。
JobFactory,根据文档描述,是 Scheduler 的一个配置项,主要用来完成Job实例的注入。默认使用的JobFactory只是简单地调用了Job 类的newInstance()方法来创建了实例。
要解决我的问题需要创建自定义的JobFactory实现。JobFactory的实现可以参考默认JobFactory的实现 SimpleJobFactory 的一些代码:
public Job newJob(TriggerFiredBundle bundle, Scheduler Scheduler) throws SchedulerException { JobDetail jobDetail = bundle.getJobDetail(); Class<? extends Job> jobClass = jobDetail.getJobClass(); try { if(log.isDebugEnabled()) { log.debug( "Producing instance of Job '" + jobDetail.getKey() + "', class=" + jobClass.getName()); } return jobClass.newInstance(); } catch (Exception e) { SchedulerException se = new SchedulerException( "Problem instantiating class '" + jobDetail.getJobClass().getName() + "'", e); throw se; } }
相当简单的内容,只需要在创建实例那块儿填写自定义的内容就可以。
写了一个示例程序来进行说明。下面是一些核心类的类图:
查看完整代码请移步 GitHub/zhyea
介绍下类图中类的作用:
如上面的解释中所说,在 MyJobFactory 的实现中, JobRegistry 是最重要的角色,在这个类中完成了 AbstractJob 实现类的实例的创建、维护和获取:
public class JobRegistry { private final MyConfig config; private final Map<Class<? extends AbstractJob>, AbstractJob> myJobMap; public JobRegistry(MyConfig config) { this.config = config; myJobMap = myJobMap(); } public AbstractJob getInstance(Class<? extends AbstractJob> clazz) { return myJobMap.get(clazz); } public Iterable<AbstractJob> jobs() { return myJobMap.values(); } private Map<Class<? extends AbstractJob>, AbstractJob> myJobMap() { Map<Class<? extends AbstractJob>, AbstractJob> m = new HashMap<>(2); m.put(MyJob.class, new MyJob(config)); return m; } }
MyJobFactory 需要提供类的实例时,可以根据类名通过 JobRegistry . getInstance 获取到 AbstractJob 子类的实例:
if (AbstractJob.class.isAssignableFrom(jobClass)) { return jobRegistry.getInstance((Class<? extends AbstractJob>) jobClass); } return jobClass.newInstance();
核心部分就是这样了。再看下AbstractJob及启动类的内容。
AbstractJob 近似于是是一个抽象模板类。在这个类里完成了JobDetail和Trigger实例的创建。当然Job接口的execute方法还是需要子类来实现。
public abstract class AbstractJob implements Job { protected MyConfig config; public AbstractJob(MyConfig config) { this.config = config; } public abstract String identity(); public abstract int intervalSeconds(); public final JobDetail job() { return newJob(this.getClass()) .withIdentity(identity()) .build(); } public final Trigger trigger() { return newTrigger() .withIdentity(identity()) .startNow() .withSchedule(simpleSchedule().withIntervalInSeconds(intervalSeconds()).repeatForever()) .build(); } }
下面是启动类的内容,在这个类里通过 scheduler . setJobFactory 完成了 MyJobFactory 实例的装配:
public static void main(String[] args) throws SchedulerException { MyConfig config = new MyConfig(1); JobRegistry registry = new JobRegistry(config); Scheduler scheduler = new StdSchedulerFactory().getScheduler(); scheduler.setJobFactory(new MyJobFactory(registry)); scheduler.start(); for(AbstractJob job : registry.jobs()){ scheduler.scheduleJob(job.job(), job.trigger()); } }
就这样了。