Spring升级至5.3.27、Quartz-scheduler升级至2.3.1后出现SchedulerConfigException错误求助
问题背景
刚完成Spring从5.1.20到5.3.27、Quartz-scheduler从2.2.1到2.3.1的版本升级,启动应用时直接抛出了如下异常栈,查了官方文档确认Quartz 2.3.1是兼容Spring 5.3.x版本的,但实在搞不清问题出在哪,求大佬帮忙分析!
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jobManager' defined in URL [jar:file:DefaultJobManager.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactory' defined in ServletContext resource [/WEB-INF/spring/app-standard-config.xml]: Invocation of init method failed; nested exception is org.quartz.SchedulerConfigException: Non-ManagedTX DataSource name not set! If your 'org.quartz.jobStore.dataSource' is XA, then set 'org.quartz.jobStore.nonManagedTXDataSource' to a non-XA datasource (for the same DB). Otherwise, you can set them to be the same.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactory' defined in ServletContext resource [/WEB-INF/spring/app-standard-config.xml]: Invocation of init method failed; nested exception is org.quartz.SchedulerConfigException: Non-ManagedTX DataSource name not set! If your 'org.quartz.jobStore.dataSource' is XA, then set 'org.quartz.jobStore.nonManagedTXDataSource' to a non-XA datasource (for the same DB). Otherwise, you can set them to be the same.
Caused by: org.quartz.SchedulerConfigException: Non-ManagedTX DataSource name not set! If your 'org.quartz.jobStore.dataSource' is XA, then set 'org.quartz.jobStore.nonManagedTXDataSource' to a non-XA datasource (for the same DB). Otherwise, you can set them to be the same.
问题根源
这个错误是Quartz 2.3.x版本对JDBC作业存储的配置规则做了强制变更:
在Quartz 2.2.x及更早版本中,只要配置了org.quartz.jobStore.dataSource,框架会默认复用该数据源处理非容器托管的事务操作;但从2.3.x开始,Quartz要求必须显式指定org.quartz.jobStore.nonManagedTXDataSource——这个参数对应的数据源是用来处理Quartz内部不需要容器事务管理的元数据操作(比如触发器状态更新、作业记录维护等),以此明确区分托管事务和非托管事务的数据源使用场景。
解决方案
根据你使用的数据源类型,分两种场景处理:
场景1:使用普通非XA数据源
如果你的org.quartz.jobStore.dataSource是普通的非XA数据源(比如Spring自带的DriverManagerDataSource或第三方连接池数据源),直接把nonManagedTXDataSource设置成和dataSource同名即可。
修改app-standard-config.xml中的SchedulerFactoryBean配置:
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <!-- 保留你已有的其他配置,比如触发器、数据源引用等 --> <property name="quartzProperties"> <props> <!-- 你原来配置的数据源名称 --> <prop key="org.quartz.jobStore.dataSource">myAppDataSource</prop> <!-- 添加该行,和上面的数据源名称保持一致 --> <prop key="org.quartz.jobStore.nonManagedTXDataSource">myAppDataSource</prop> <!-- 其他Quartz基础配置,比如作业存储类型 --> <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop> </props> </property> <!-- 数据源映射,确保你的数据源Bean已提前定义 --> <property name="dataSources"> <map> <entry key="myAppDataSource" value-ref="yourExistingDataSourceBean"/> </map> </property> </bean>
场景2:使用XA分布式事务数据源
如果你的org.quartz.jobStore.dataSource是XA类型的数据源(用于分布式事务场景),则需要额外配置一个非XA数据源(必须连接同一个数据库),然后把nonManagedTXDataSource指向这个新的非XA数据源:
- 先在Spring配置中定义非XA数据源Bean:
<bean id="nonManagedXADataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="jdbcUrl" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> <!-- 其他连接池配置,比如最大连接数、超时时间等 --> </bean>
- 再修改
SchedulerFactoryBean的Quartz属性配置:
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <!-- 保留你已有的其他配置 --> <property name="quartzProperties"> <props> <!-- 原有的XA数据源 --> <prop key="org.quartz.jobStore.dataSource">myXADataSource</prop> <!-- 指向新配置的非XA数据源 --> <prop key="org.quartz.jobStore.nonManagedTXDataSource">nonManagedXADataSource</prop> <!-- 分布式事务场景下的作业存储类型 --> <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreCMT</prop> <!-- 其他XA相关配置 --> </props> </property> <!-- 映射两个数据源 --> <property name="dataSources"> <map> <entry key="myXADataSource" value-ref="yourXADataSourceBean"/> <entry key="nonManagedXADataSource" value-ref="nonManagedXADataSource"/> </map> </property> </bean>
验证说明
配置完成后重启应用,这个SchedulerConfigException应该就能解决了。本质上就是Quartz新版本对事务边界的处理更严谨,强制区分了不同事务场景下的数据源使用规则。
备注:内容来源于stack exchange,提问作者hamza moslah

