在SpringBoot 2.0或更早版本中,默认允许bean定义被覆盖。并且没有提供通过属性配置的方式来禁止这种行为,设置spring.main.allow-bean-definition-overriding=false
并不会生效,因为它是2.1.0版本开始引入的。
我们可以通过编程的方式,在启动类中创建一个新的ApplicationContextInitializer
,并且重写它的initialize()
方法以禁用bean定义覆盖。
public class DemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
application.addInitializers((ApplicationContextInitializer<GenericApplicationContext>) applicationContext ->
applicationContext.setAllowBeanDefinitionOverriding(false)
);
application.run(args);
}
}
从SpringBoot 2.1.0开始,默认禁用bean名称相同时的覆盖行为。
可以通过配置以下属性重新启用(不推荐):
spring.main.allow-bean-definition-overriding=true
如果有多个名称相同的bean时,添加@Primary
注解的方式无法解决bean覆盖的问题。@Primary
适用于存在多个类型bean的情况,它会将其中一个bean指定为依赖注入中优先提供的bean。
许多框架会自动配置默认的bean,同时设置为条件注入,因此即使创建名称相同的bean,也不存在覆盖的问题。例如在Spring JacksonAutoConfiguration
源码中,配置ObjectMapper
bean的代码如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
return builder.createXmlMapper(false).build();
}
}
使用了@ConditionalOnMissingBean
注解,表示只有在容器中不存在ObjectMapper
类型的bean时才会创建该bean。
当设置允许bean定义被覆盖时,哪个bean会被最终提供是难以预测的,因为bean的创建顺序主要由运行时的依赖关系决定的,允许bean重写可能会产生意料之外的行为,除非足够了解bean的依赖层次结构。