Spring Boot 2中XML定义Bean时StepScope Bean重复注册问题
现象
在Spring Boot 2环境下,仅通过SpringApplication.setSources()加载XML配置文件启动应用时,会抛出BeanDefinitionOverrideException异常;但改用Java配置方式,或通过@ImportResource引入同一份XML配置时,应用可正常启动。当前无法升级到Spring Boot 3。
错误日志
*************************** APPLICATION FAILED TO START *************************** Description: The bean 'scopedTarget.reader', defined in BeanDefinition defined in file [/Users/username/IdeaProjects/testing-spring-batch/target/classes/org/example/Reader.class], could not be registered. A bean with that name has already been defined in file [/Users/username/IdeaProjects/testing-spring-batch/target/classes/org/example/Reader.class] and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
相关代码
Main.java
@SpringBootApplication @EnableBatchProcessing public class Main { public static void main(String[] args) { SpringApplication app = new SpringApplication(Main.class); app.setSources(Collections.singleton("classpath:launch-context.xml")); System.exit(SpringApplication.exit(app.run(args).getParent())); } }
Reader.java
@Component @StepScope public class Reader implements ItemReader<String> { // return a list of string }
XML配置(launch-context.xml)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <batch:job id="test"> <batch:step id="companyStep"> <batch:tasklet> <batch:chunk reader="reader" processor="processor" writer="writer" commit-interval="1" /> </batch:tasklet> </batch:step> </batch:job> </beans>
排查结论
scopedTarget.reader这个StepScope代理Bean被重复注册:一次来自Spring注解自动扫描(@Component+@StepScope),另一次来自Step Scope后置处理器的调用。- Java配置场景下,
ScopeConfiguration类会将Step Scope的自动代理标志设为false,避免重复代理;但XML配置场景下,StepScope通过CoreNamespaceUtils.checkForScope()调用原生构造器创建,自动代理默认开启,导致重复生成代理Bean。 @ImportResource引入XML时无异常,是因为资源在应用上下文刷新(Bean定义解析完成后)才被处理,时机与注解扫描不冲突;但当前场景需要动态传入XML资源,无法使用该方式。
规避方案
针对动态传入XML的场景,可选择以下方案:
1. 启用Bean定义覆盖(最简单但需注意风险)
直接允许Bean定义覆盖,可通过代码或配置文件设置:
- 代码方式:
SpringApplication app = new SpringApplication(Main.class); app.setAllowBeanDefinitionOverriding(true); // 开启覆盖 app.setSources(Collections.singleton("classpath:launch-context.xml"));
- 配置文件方式(application.properties):
spring.main.allow-bean-definition-overriding=true
注意:该方式会允许同名Bean覆盖,可能引发意外的Bean替换风险,需确认业务场景无冲突。
2. 自定义StepScope并关闭自动代理
通过配置类手动注册StepScope,设置autoProxy=false,替代XML自动生成的StepScope:
@Configuration public class CustomStepScopeConfig { @Bean public static StepScope stepScope() { StepScope stepScope = new StepScope(); stepScope.setAutoProxy(false); // 关键:关闭自动代理,避免重复生成scopedTarget bean stepScope.setScopeName("step"); return stepScope; } }
确保该配置类被Spring扫描到(放在@SpringBootApplication所在包或子包下),即可让XML加载时使用自定义的StepScope,避免重复代理。
3. 调整XML资源加载时机
模拟@ImportResource的延迟加载逻辑,通过ApplicationContextInitializer在上下文初始化阶段加载XML,而非直接设置为应用源:
public static void main(String[] args) { SpringApplication app = new SpringApplication(Main.class); app.addInitializers((ApplicationContextInitializer<GenericApplicationContext>) context -> { // 手动加载XML Bean定义 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context); reader.loadBeanDefinitions(new ClassPathResource("launch-context.xml")); }); System.exit(SpringApplication.exit(app.run(args).getParent())); }
该方式让XML Bean定义的加载时机与注解扫描协调,避免StepScope后置处理器重复触发代理。
4. 调整Reader的Scope配置方式
移除Reader类上的@Component和@StepScope注解,改为在XML中直接定义Reader Bean并指定StepScope:
<!-- 在launch-context.xml中添加 --> <bean id="reader" class="org.example.Reader" scope="step"/>
这样仅通过XML生成scopedTarget代理Bean,避免注解扫描带来的重复注册。
内容的提问来源于stack exchange,提问作者Antho Christen

