每天学习一点点之 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.mzph.cn/news/611643.shtml

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

相关文章

k8s-----存储卷(数据卷)

容器内的目录和宿主机的目录进行挂载。 容器的生命状态是短站的&#xff0c;delete删除&#xff0c;k8s用控制创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会回复到初始状态。 一旦回到初始状态&#xff0c;所有的后天编辑的文件都会消失。 容器和节点之间创…

飞致云1panel v1.9.2 + 雷池WAF社区版 v4.0

可能有许多人都有这个需求&#xff1a;为自己的个人站点套上WAF&#xff0c;增加安全性&#xff0c;本文将介绍如何将1panel面板深度结合长亭雷池防火墙&#xff0c;实现为个人站点套上WAF并且自动续签ssl证书。 由于1panel和雷池WAF功能更新较快&#xff0c;上一篇文章已经不…

Kubernets(K8S)启动和运行01 快速入门

简介 Kubernetes is an open source orchestrator for deploying containerized applications. It was originally developed by Google, inspired by a decade of experience deploying scalable, reliable systems in containers via application-oriented APIs. Kubernete…

Open CASCADE学习|模块组成

OpenCASCADE由七个模块组成&#xff0c;分别如下&#xff1a; Foundation Classes基础类 Modeling Data 建模数据 Modeling Algorithms 建模算法 Visualization 可视化 Data Exchange 数据交换 Application Framework 程序框架 Kernel Classes 核心类 2D Geometry 二维几…

nginx(1.13.7)首次安装出现:【make: *** 没有规则可以创建“default”需要的目标“build” 问题】解决措施

目录 前言&#xff1a; 一.龙蜥&#xff08;Anolis&#xff09;操作系统上安装GCC 1.安装gcc 2.检验安装 二.安装出现 make&#xff1a; *** 没有规则可以创建“default”需要的目标“build” 问题 1.解压安装nginx 2.安装出现问题展示 3.解决措施 4.重新编译进行安装 5…

【Sublime Text】| 01——下载安装注册

系列文章目录 【Sublime Text】| 01——下载软件安装并注册 【Sublime Text】| 02——常用插件安装及配置 失败了也挺可爱&#xff0c;成功了就超帅。 文章目录 前言1. 下载2. 安装3. 注册3.1 通过修改应用程序注册3.2 通过替换应用程序注册 感谢 前言 轻量代码编辑器有很多 之…

国家发改委:《电能质量管理办法(暂行)》2024年4月1日起施行

中华人民共和国国家发展和改革委员会令 第8号 《电能质量管理办法(暂行)》已经2023年12月26日第7次委务会议审议通过,现予公布,自2024 年4月1日起施行。 主任 郑栅洁 2023年12月27日 电能质量管理办法&#xff08;暂行&#xff09; 第一章 总则 第一条 为加强电能质量管理&…

中小型企业如何在当今的商业环境中占得一席之地?

在电商新零售的浪潮下&#xff0c;一种全新的消费模式正在崭露头角——消费增值模式。这种模式不仅改变了消费者的购物体验&#xff0c;更在电商领域掀起了一场革命。本文将通过具体数据&#xff0c;为您揭示消费增值模式的魅力。 一、消费增值模式的奥秘 消费增值模式的核心在…

【DevOps-08-2】Harbor的基本操作

一、简要描述 Harbor作为镜像仓库,主要的交互方式就是将镜像上传到Harbor上,以及从Harbor上下载指定镜像 在传输镜像前,可以先使用Harbor提供的权限管理,将项目设置为私有项目,并对不同用户设置不同角色,从而更方便管理镜像。 二、Harbor添加用户和项目 1、添加Harbor用…

一篇文告诉你:到底该如何搭建好企业知识库

全球化的竞争环境和瞬息万变的市场使得企业需要一种管理和整合各种信息与知识的方式。这就是企业知识库&#xff0c;一种核心资源&#xff0c;能够帮助企业提升工作效能&#xff0c;并更好地支持决策、创新和学习。但是&#xff0c;如何优化知识库&#xff0c;让它成为企业的有…

spark基础--学习笔记

1 spark 介绍 1.1 spark概念 Apache Spark是专为大规模数据处理而设计的快速通用的分布式计算引擎&#xff0c;是开源的类Hadoop MapReduce的通用分布式计算框架。和MapReduce一样&#xff0c;都是完成大规模数据的计算处理。 简而言之&#xff0c;Spark 借鉴了 MapReduce思…

【链表】力扣206反转链表

题目 力扣206反转链表 思路图解 代码实现 双指针代码实现 public static ListNode reverseList(ListNode head) {// 初始化pre&#xff0c;curListNode pre null;ListNode cur head;// 当cur为null时&#xff0c;说明反转结束while(cur ! null) {// 临时保存cur.next节点…

linux开发板静态IP无法ping通外网

硬件平台&#xff1a;韦东山的6ull开发板 问题&#xff1a; 使用网线直连路由器&#xff0c;动态获取IP时能ping通外网&#xff1b; 改为静态IP时&#xff0c;能ping通局域网&#xff0c;但无法ping通外网。 改为静态IP&#xff1a;修改/etc/network/interfaces 测试&#…

CentOS本地部署SQL Server数据库无公网ip环境实现远程访问

文章目录 前言1.安装GeoServer2. windows 安装 cpolar3. 创建公网访问地址4. 公网访问Geo Servcer服务5. 固定公网HTTP地址 前言 GeoServer是OGC Web服务器规范的J2EE实现&#xff0c;利用GeoServer可以方便地发布地图数据&#xff0c;允许用户对要素数据进行更新、删除、插入…

Linux系统——测试端口连通性方法

目录 一、TCP端口连通性测试 1、ssh 2、telnet&#xff08;可能需要安装&#xff09; 3、curl 4、tcping&#xff08;需要安装&#xff09; 5、nc&#xff08;需要安装&#xff09; 6、nmap&#xff08;需要安装&#xff09; 二、UDP端口连通性测试 1、nc&#xff08;…

从0到1实战微服务架构之Nacos下载安装

目录 一、前言 二、Nacos概述 三、Nacos架构 3.1 Open API 3.2 Config Service 3.3 Naming Service 3.4 Nacos Core 3.5 Consistency Protocol 四、Nacos部署实践 4.1 Nacos下载 4.2 Nacos部署 五、总结 一、前言 Nacos是一个开源的、易于使用的、功能丰富的平台&a…

19道ElasticSearch面试题(很全)

点击下载《19道ElasticSearch面试题&#xff08;很全&#xff09;》 1. elasticsearch的一些调优手段 1、设计阶段调优 &#xff08;1&#xff09;根据业务增量需求&#xff0c;采取基于日期模板创建索引&#xff0c;通过 roll over API 滚动索引&#xff1b; &#xff08;…

谓词-量词、主析取、主和取范式、前束范式、推理证明

这部分内容&#xff0c;主要需要掌握谓词推理&#xff0c;而前提是掌握将自然语言符号化为谓词、用量词来限定辖域&#xff0c;量词的消去、剩下就是推理过程。还需要掌握的是主析取、主和取范式和前束范式。 存在量词∃&#xff1a;至少有一个 全称量词∀&#xff1a;全都是…

Linux驱动学习—输入子系统

1、什么是输入子系统&#xff1f; 输入子系统是Linux专门做的一套框架来处理输入事件的&#xff0c;像鼠标&#xff0c;键盘&#xff0c;触摸屏这些都是输入设备&#xff0c;但是这邪恶输入设备的类型又都不是一样的&#xff0c;所以为了统一这些输入设备驱动标准应运而生的。…

springCould中的Bus-从小白开始【11】

目录 &#x1f9c2;1.Bus是什么❤️❤️❤️ &#x1f32d;2.什么是总线❤️❤️❤️ &#x1f953;3.rabbitmq❤️❤️❤️ &#x1f95e;4.新建模块3366❤️❤️❤️ &#x1f373;5.设计思想 ❤️❤️❤️ &#x1f37f;6.添加消息总线的支持❤️❤️❤️ &#x1f9…