每天学习一点点之 Spring Boot 1.x 升级 2.x 之 allowBeanDefinitionOverriding

最近组内大佬正在进行 Spring Boot 版本的升级,从 1.x 版本升级到 2.x 版本。在查看代码变更时,我注意到我之前编写的一个名为 ShardingRuleStrategy 的类被添加了 @Primary 注解。这个类在原来的代码中被标记为 @Component,同时也在 API 中被定义:

@Bean
public ShardingRuleStrategy shardingRuleStrategy() {return new ShardingRuleStrategy(shardingDataSourcePropertiesResolver.getMetaData());
}

这就引发了一个问题:在 Spring Boot 1.x 版本中,定义两个同名的 Bean 是被允许的,不会引发任何冲突。但是在 Spring Boot 2.x 版本中,这样做就会导致冲突。

那这究竟是怎么回事呢?

复现

首先需要创建一个 Spring Boot 1.x 的项目。由于 Spring Boot 正在快速迭代,无法直接使用 [https://start.spring.io/](vscode-file://vscode-app/Applications/Visual Studio Code.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/workbench.html) 来创建 1.x 版本的项目,因此需要手动进行构建。

构建完成后,启动项目,可以在控制台看到以下日志输出:

  .   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::       (v1.5.22.RELEASE)

定义 Bean:

@Component
public class Test {
}
@SpringBootApplication
public class App {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(App.class, args);String[] beanNames = context.getBeanDefinitionNames();Test bean = context.getBean(Test.class);System.out.println(bean);System.out.println("List :");for (String beanName : beanNames) {System.out.println(beanName);}}@Beanpublic Test test(){return new Test();}
}

运行后输出:

demo.sb1.Test@61d9efe0
List :
...
test
...

可以看到,Spring 容器中只有一个 Test Bean,没有出现任何异常。

接下来需要创建一个 Spring Boot 2.x 的项目。只需要在 pom.xml 文件中修改 Spring Boot 的版本即可:

  <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><!-- <version>1.5.22.RELEASE</version>--></parent>

重新启动工程,发现报错了:

  .   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::                (v2.6.3)
...
***************************
APPLICATION FAILED TO START
***************************Description:The bean 'test', defined in demo.sb1.App, could not be registered. A bean with that name has already been defined in file [/Users/dongguabai/Downloads/demosb1/target/classes/demo/sb1/Test.class] and overriding is disabled.Action:Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

这意味着名为 ‘test’ 的 Bean 已经在 demo.sb1.App 中定义,并且不允许被覆盖。Spring Boot 也给出了修复这个问题的方法,即通过设置 spring.main.allow-bean-definition-overriding 配置项。这个配置项就是接下来要讨论的主题。

allowBeanDefinitionOverriding 配置

可以看到,这个配置项的前缀是 spring.main,这是用来控制 Spring Boot 应用的主要行为的配置项。

其中最经典的一个例子就是 spring.main.banner-mode,它可以用来控制 Spring Boot 在启动时是否显示 banner。如果想了解更多的配置项,可以在 SpringApplication 类中进行查找。

接下来看一下 spring.main.allow-bean-definition-overriding 配置项的实现。可以在 org.springframework.boot.SpringApplication#prepareContext 方法中找到相关的代码:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {...if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);if (beanFactory instanceof DefaultListableBeanFactory) {//为DefaultListableBeanFactory设置allowBeanDefinitionOverriding属性((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}}if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}
...
}

可以看到就是基于 DefaultListableBeanFactoryallowBeanDefinitionOverriding 属性实现的。

版本疑问

可以看到,spring.main.allow-bean-definition-overriding 配置项是基于 DefaultListableBeanFactoryallowBeanDefinitionOverriding 属性实现的。

然而 DefaultListableBeanFactory 是属于 Spring 的一部分,而 allowBeanDefinitionOverriding 属性在 Spring 的早期版本中就已经存在了。Spring Boot 2.x 需要至少 Spring Framework 5.0 版本。这意味着,Spring Boot 1.x 实际上也可以实现这个属性对应的功能,只不过 Spring Boot 官方没有提供现成的配置项而已

那就基于 Spring Boot 1.x 的版本实现一下,在启动类加一个自己定义的 ApplicationContextInitializer,并且设置 allowBeanDefinitionOverriding 属性为 false

@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication application = new SpringApplication(App.class);application.addInitializers(new MyApplicationContextInitializer());ApplicationContext context = application.run(args);String[] beanNames = context.getBeanDefinitionNames();Test bean = context.getBean(Test.class);System.out.println(bean);System.out.println("List :");for (String beanName : beanNames) {System.out.println(beanName);}}static class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {if (applicationContext.getBeanFactory() instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) applicationContext.getBeanFactory()).setAllowBeanDefinitionOverriding(false);}}@Beanpublic Test test() {return new Test();}}
}

启动:

  .   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::       (v1.5.22.RELEASE)
...
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'test' defined in demo.sb1.App$CustomApplicationContextInitializer: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=demo.sb1.App$CustomApplicationContextInitializer; factoryMethodName=test; initMethodName=null; destroyMethodName=(inferred); defined in demo.sb1.App$CustomApplicationContextInitializer] for bean 'test': There is already [Generic bean: class [demo.sb1.Test]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [/Users/dongguabai/Downloads/demosb1/target/classes/demo/sb1/Test.class]] bound.at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:807) ~[spring-beans-4.3.25.RELEASE.jar:4.3.25.RELEASE]...

可以看到出现了不允许 Bean 被重复定义的异常。

总结

本文对 Spring Boot 2.x 中新增的 spring.main.allow-bean-definition-overriding 配置进行了简要分析,并通过实际示例说明了这个配置的底层原理并非源自 Spring Boot 2.x。实际上,即使在 Spring Boot 1.x 中,我们也可以实现这个功能,只是 Spring Boot 官方并没有提供现成的配置项。

欢迎关注公众号:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/338021.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android开发基础(一)

Android开发基础&#xff08;一&#xff09; 本篇主要是从Android系统架构理解Android开发。 Android系统架构 Android系统的架构采用了分层的架构&#xff0c;共分为五层&#xff0c;从高到低分别是Android应用层&#xff08;System Apps&#xff09;、Android应用框架层&a…

Python基础知识:整理9 文件的相关操作

1 文件的打开 # open() 函数打开文件 # open(name, mode, encoding) """name: 文件名&#xff08;可以包含文件所在的具体路径&#xff09;mode: 文件打开模式encoding: 可选参数&#xff0c;表示读取文件的编码格式 """ 2 文件的读取 文…

Qt During startup program exited with code 0xc0000135

网上试了好多办法没有用&#xff0c;可以试试在pro目录下加入如图所示的.dll 可以下个everything搜索整个电脑查看是否有上述dll&#xff0c;如果没有也可以网上下载或者点击连接

文心大模型融入荣耀MagicOS!打造大模型“端云协同”创新样板

2024年1月10日&#xff0c;在荣耀MagicOS 8.0发布会及开发者大会上&#xff0c;荣耀终端有限公司CEO赵明宣布了“百模生态计划”&#xff0c;并与百度集团执行副总裁、百度智能云事业群总裁沈抖共同宣布&#xff0c;百度智能云成为荣耀大模型生态战略合作伙伴。 沈抖在现场演讲…

【UE Niagara学习笔记】05 - 喷射火焰顶部的蓝色火焰

在上一篇博客&#xff08;【UE Niagara学习笔记】04 - 火焰喷射时的黑烟效果&#xff09;的基础上继续实现在火焰喷射的起点位置生成蓝色火焰的效果。 目录 效果 步骤 1. 创建新的发射器 2. 减少粒子生成数量 3. 减小粒子初始大小 4. 减少粒子喷射距离 5. 减少粒子初始…

网络服务DHCP与DNS

一.DHCP 1.DHCP概念 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff09; 通常被应用在大型的局域网络环境中&#xff0c;主要作用是集中地管理、分配IP地址&#xff0c;使网络环境中的主机动态的获得IP地址、Gateway地址、DNS服务…

xss-labs(10-16)

level10:欢迎来到level10 尝试注入 <script>alert(欢迎来钓鱼)</script> 寻找注入点 让表单显示出来 随便输入一个字符康康 url出现了变化</

分布式全局id

分布式全局id snowflake 算法是 twitter 开源的分布式 id 生成算法&#xff0c;采用 Scala 语言实现&#xff0c;是把一个 64 位的 long 型的 id&#xff0c;1 个 bit 是不用的&#xff0c;用其中的 41 bits 作为毫秒数&#xff0c;用 10 bits 作为工作机器 id&#xff0c;12 …

职场日常英语口语,成人英语培训学校,柯桥学英语推荐哪里

“玩手机”用英语怎么说&#xff1f;你的第一反应是不是&#xff1a;play the phone&#xff1f; 在英语中&#xff0c;play这个动词通常表示“玩耍、娱乐、操纵”等意思&#xff0c;而手机是一种工具&#xff0c;不是玩耍的对象。 换句话说&#xff0c;我们“玩手机”&#xf…

threejs 光带扩散动画

目录 一、创建光带 (1) 设置光带顶点 (2) 设置光带顶点透明度属性 二、光带动画 完整代码 html文件代码 js文件代码 最后展示一下项目里的效果&#xff1a; 最近项目中要求做一段光带效果动画&#xff0c;尝试着写了一下&#xff0c;下面是本次分享光带扩散动画的效果预…

【JAVA】Java 中 Set集合常用方法

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 常用方法 代码示例 结语 我的其他博客 前言 Java中的Set接口提供了一种不允许包含重复元素的集合。常用的实现类有HashS…

小H靶场笔记:DC-9

DC-9 January 10, 2024 10:02 AM Tags&#xff1a;knockd Owner&#xff1a;只惠摸鱼 信息收集 使用arp-scan和nmap扫描C段存活主机&#xff0c;探测靶机ip&#xff1a;192.168.199.139&#xff0c;只有80端口开放。 再扫一下靶机端口服务的情况吧。发现22端口是被过滤的状…