Quartz 2.2.1中JobDetailFactoryBean传递JobDataMap失效问题排查
我之前也遇到过类似的问题,结合你的配置和代码来看,主要问题出在Quartz持久化存储(JobStoreTX)下,Spring无法自动注入Job属性,且JobDataMap未被正确持久化/读取。下面是具体的原因分析和解决方案:
一、核心原因
- Quartz实例化Job而非Spring管理:当使用JDBC类型的JobStore(如
JobStoreTX)时,Quartz会直接从数据库加载Job定义并自己实例化MyJob对象,而非由Spring容器管理。这导致Spring的依赖注入(包括从jobDataAsMap注入param)无法自动生效,所以你的param字段为null。 - JobDataMap未被正确持久化/读取:如果Quartz数据库表结构不正确(比如
JOB_DATA字段类型不兼容),或者Spring注册JobDetail时未正确传递JobDataMap到Quartz,就会出现jobDataMap.size()=0的情况。
二、分步解决方案
1. 修复Quartz数据库表结构(关键前提)
确保你使用的是Quartz 2.2.1对应的MSSQL数据库表结构,尤其是QRTZ_JOB_DETAILS表的JOB_DATA字段:
- 该字段应为
VARBINARY(MAX)类型(用于存储序列化的JobDataMap) - 如果是从Quartz 1.x升级的旧表结构,务必替换为Quartz 2.x的官方表结构(可从Quartz官方包中获取对应版本的SQL脚本)
2. 配置自定义Spring JobFactory,让Quartz创建的Job支持Spring注入
创建一个自定义的SpringBeanJobFactory子类,让Quartz实例化的Job对象能被Spring自动装配:
import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.scheduling.quartz.SpringBeanJobFactory; public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(ApplicationContext applicationContext) { this.beanFactory = applicationContext.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 让Quartz创建Job实例 Object jobInstance = super.createJobInstance(bundle); // 交给Spring自动装配(包括注入JobDataMap中的属性) beanFactory.autowireBean(jobInstance); return jobInstance; } }
然后在Spring配置中,将这个JobFactory注入到SchedulerFactoryBean:
<bean id="autowiringJobFactory" class="com.my.AutowiringSpringBeanJobFactory" /> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="jobDetails"> <list> <ref bean="myJob" /> </list> </property> <property name="triggers"> <list> <ref bean="myTrigger" /> </list> </property> <property name="quartzProperties" ref="quartzProperties"/> <!-- 配置自定义JobFactory --> <property name="jobFactory" ref="autowiringJobFactory" /> </bean>
3. 优化JobDataMap的读取方式
在executeInternal方法中,尝试使用getMergedJobDataMap()替代直接从JobDetail获取,它会合并JobDetail和Trigger的JobDataMap(虽然你的Trigger没有配置,但这是更通用的方式):
@Override protected void executeInternal(JobExecutionContext jobContext) throws JobExecutionException { // 使用合并后的JobDataMap JobDataMap jobDataMap = jobContext.getMergedJobDataMap(); log.debug("size of jobdatamap {}", jobDataMap.keySet().size()); log.debug("quartz param from map {}", jobDataMap.getString("param")); log.debug("quartz param from field {}", param); // some code }
4. 调试验证JobDetail注册
在启动时添加日志,验证Spring创建的JobDetail是否包含正确的JobDataMap:
// 可以在Spring配置类或者启动类中注入JobDetailFactoryBean的实例,打印JobDataMap @Autowired private JobDetailFactoryBean myJob; @PostConstruct public void checkJobDetail() { System.out.println("JobDataMap content: " + myJob.getObject().getJobDataMap()); }
如果这里打印出{param=myParam},说明Spring端的配置是正确的,问题出在Quartz数据库存储或读取环节。
三、额外注意事项
- 确保你的Spring版本与Quartz 2.2.1兼容(建议使用Spring 3.2.x及以上版本,它们对Quartz 2.x的支持更完善)
@PersistJobDataAfterExecution注解会让Quartz在Job执行后将JobDataMap回写到数据库,如果你的业务不需要修改JobDataMap,可以暂时去掉该注解,排除干扰- 检查数据库连接是否正常,确保Quartz能正确读写数据库中的Job定义
内容的提问来源于stack exchange,提问作者Darshana
相关产品推荐
相关产品推荐

