Spring 4.3+Quartz 2.2.3集群Job的JUnit测试IOC配置方案咨询
Spring 4.3 + Quartz 2.2.3 集群Job集成Spring IOC并支持JUnit测试方案
我之前刚好处理过类似的场景,你的痛点我太懂了——直接用@Component把Job交给Spring管理会破坏Quartz的集群调度逻辑(比如JobStore的分布式协调、实例生命周期管理),但又需要Spring的IOC来方便做JUnit测试,其实有个成熟的解决方案,核心是自定义Quartz的JobFactory桥接Spring依赖注入,同时保留Quartz原生的集群能力。
核心思路
Quartz默认会自己创建Job实例,不会感知Spring的IOC容器;而如果用@Component让Spring接管Job实例,Quartz的集群调度机制会失效(因为Spring实例的Job不受Quartz的JobStore集群管理)。所以我们需要让Quartz负责Job的生命周期管理,但在创建Job实例时,委托Spring来注入依赖。
具体实现步骤
1. 自定义Spring兼容的JobFactory
继承Quartz的AdaptableJobFactory,重写createJobInstance方法,利用Spring的ApplicationContext来完成依赖注入:
import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; @Component public class SpringQuartzJobFactory extends AdaptableJobFactory { @Autowired private ApplicationContext applicationContext; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 让Quartz创建Job实例 Object jobInstance = super.createJobInstance(bundle); // 让Spring为这个实例注入依赖 applicationContext.getAutowireCapableBeanFactory().autowireBean(jobInstance); return jobInstance; } }
2. 配置Quartz Scheduler(集群模式+Spring集成)
用SchedulerFactoryBean配置Quartz,指定自定义的JobFactory,同时配置集群相关属性:
import org.quartz.Scheduler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import javax.sql.DataSource; import java.util.Properties; @Configuration public class QuartzClusterConfig { @Autowired private SpringQuartzJobFactory springQuartzJobFactory; @Autowired private DataSource dataSource; // 共用的集群数据源 @Bean public SchedulerFactoryBean schedulerFactoryBean() { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setJobFactory(springQuartzJobFactory); factory.setDataSource(dataSource); factory.setQuartzProperties(quartzClusterProperties()); // 可以添加自定义的JobDetail和Trigger,或者让Quartz从数据库读取(集群模式推荐) return factory; } @Bean public Scheduler scheduler() { return schedulerFactoryBean().getScheduler(); } private Properties quartzClusterProperties() { Properties props = new Properties(); // 调度器配置 props.put("org.quartz.scheduler.instanceName", "ClusterScheduler"); props.put("org.quartz.scheduler.instanceId", "AUTO"); // 自动生成唯一实例ID // 线程池配置 props.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); props.put("org.quartz.threadPool.threadCount", "10"); // 集群JobStore配置(必须用JobStoreTX或JobStoreCMT) props.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX"); props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate"); props.put("org.quartz.jobStore.isClustered", "true"); props.put("org.quartz.jobStore.clusterCheckinInterval", "20000"); // 集群节点心跳间隔 props.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); // 数据库表前缀,要和Quartz初始化脚本一致 return props; } }
3. 编写Job类(无需@Component)
Job类不需要加@Component,让Quartz管理实例,但可以正常使用@Autowired注入Spring Bean:
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; public class ClusterTestJob implements Job { @Autowired private YourBusinessService businessService; // 依赖的Spring Bean @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 执行业务逻辑 businessService.doSomething(); } }
4. JUnit测试方案
利用Spring的测试框架,加载测试上下文,直接调用Job的方法或者模拟调度:
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {QuartzClusterConfig.class, YourTestConfig.class}) public class ClusterJobTest { @Autowired private ClusterTestJob clusterTestJob; @Autowired private YourBusinessService mockBusinessService; // 可以用MockBean替换真实服务 @Test public void testJobExecution() throws Exception { // 直接调用Job的execute方法,依赖已经被Spring注入 clusterTestJob.execute(null); // 验证业务逻辑执行结果 // verify(mockBusinessService).doSomething(); } }
关键注意事项
- 所有集群节点的Quartz配置必须完全一致(比如
instanceName、jobStore相关属性),并且共用同一个数据库。 - 确保数据库中已经创建了Quartz的集群表(可以从Quartz官方包的
docs/dbTables目录找到对应数据库的初始化脚本)。 - 不要给Job类加
@Scope("prototype"),Quartz会根据调度需求创建实例,自定义JobFactory会处理每次实例的依赖注入。
内容的提问来源于stack exchange,提问作者Marcelo Ferreira
相关产品推荐
相关产品推荐

