springboot中@bean注解的创建和使用

bean的创建顺序

在Spring Boot中,当一个配置类(使用@Configuration注解的类)中定义了多个bean时,这些bean的创建顺序并不完全由它们在类中的声明顺序决定。Spring框架在创建和管理bean时,遵循了复杂的依赖注入和生命周期管理规则,这些规则决定了bean的创建和初始化顺序。

以下是文心一言给出的一些影响bean创建顺序的主要因素(我直接复制过来):

  • 依赖关系:Spring容器会根据bean之间的依赖关系来决定创建顺序。如果一个bean依赖于另一个bean,那么被依赖的bean会首先被创建。Spring通过构造函数、setter方法或字段注入等方式来识别这些依赖关系。
  • @DependsOn注解:你可以使用@DependsOn注解来显式指定一个bean依赖于其他一个或多个bean。被@DependsOn注解指定的bean会在当前bean之前被创建。
  • @Order或实现Ordered接口:虽然这些主要用于排序多个相同类型的bean(例如,多个实现了同一接口的bean),但它们在某些情况下也可能间接影响bean的创建顺序,尤其是当这些bean之间存在依赖关系时。
  • @Bean的注册顺序:在配置类中,虽然bean的声明顺序不是决定性因素,但在没有其他依赖关系或显式排序的情况下,* Spring可能会按照它们在配置类中声明的顺序来创建bean。但是,这种顺序并不是严格保证的,特别是当存在复杂的依赖关系时。
  • 初始化回调:Spring提供了几种初始化回调方法(如@PostConstruct注解的方法或实现了InitializingBean接口的afterPropertiesSet方法),这些方法在bean的所有必要属性被容器设置之后被调用。这些回调的执行顺序也受bean之间的依赖关系影响。
  • 懒加载(Lazy Initialization):如果bean被标记为懒加载(通过@Lazy注解或全局配置),那么它只会在首次被请求时创建,这可能会影响bean的创建顺序。
  • 总结来说,Spring Boot中配置类中多个bean的创建顺序主要由bean之间的依赖关系决定,而不仅仅是它们在配置类中的声明顺序。因此,在设计应用时,应该尽量避免对bean创建顺序的隐式依赖,而是通过显式的依赖关系或配置来管理bean的创建和初始化顺序。

bean注解的创建

首先这个注解在方法上使用,也可以在注解使用,这里只介绍在方法上使用的情况
在这里插入图片描述
在方法上使用很简单,只需要把它放在方法上就行
e.g

    @Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}
  • 这样如果在类中当做类属性使用,我们直接使用@Autowired注解注入就好了,

那如果是下面这样呢,这个

    @Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource(DataSource dataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), dataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(dataSource, targetDataSources);}

这样根据基于springboot的自动装配类型中的基于类型装配,可以找到我们上面创建的那个DataSource类型的bean

spring的依赖注入

实际上在Spring框架中,当你使用@Bean注解来声明一个bean的创建方法时,该方法中的参数并不是直接从某个地方“自动”获取的,而是根据Spring的依赖注入(DI)机制来解决的。Spring容器在创建bean时,会分析@Bean方法中的参数,并尝试通过以下几种方式来解决这些参数的依赖:

  • 自动装配(Autowiring):
  1. 基于类型(byType):Spring会尝试在容器中查找与参数类型相匹配的bean。如果容器中只有一个bean匹配该类型,Spring会自动注入这个bean。如果有多个bean匹配,并且没有使用@Qualifier注解来指定具体的bean名称,那么Spring会抛出异常,因为它不知道应该注入哪一个bean。
  2. 基于名称(byName):如果你的@Bean方法参数名与容器中某个bean的名称相匹配,并且Spring的配置中启用了基于名称的自动装配(这通常是默认行为),那么Spring会尝试注入这个bean。不过,需要注意的是,在@Bean方法中使用基于名称的自动装配并不是非常直观,因为@Bean方法的参数名在编译后可能会被优化或更改,这取决于JVM和编译器的设置。因此,更推荐使用基于类型的自动装配。
  • 通过方法参数中的注解:
  1. 如果@Bean方法的参数上使用了如@Qualifier、@Value等注解,Spring会根据这些注解来解析参数的值。例如,@Qualifier注解可以用来指定应该注入哪个bean(在有多个候选bean的情况下)。@Value注解则通常用于注入配置文件中的值(如属性文件中的值)。
  2. 通过构造函数或setter方法:
    需要注意的是,虽然这里讨论的是@Bean方法中的参数,但通常我们不会在@Bean方法内部直接创建依赖对象(即参数所代表的bean)。相反,我们会让Spring通过构造函数或setter方法将这些依赖注入到我们的bean中。然而,对于@Bean方法本身,其参数是通过上述的依赖注入机制来解决的。
  3. Java配置和@Configuration类:
    在@Configuration注解的类中,@Bean方法之间可以相互引用,因为Spring会确保在调用一个@Bean方法之前,它所依赖的所有bean都已经被创建和初始化。这种机制使得我们可以在@Bean方法中引用其他@Bean方法声明的bean。
    总之,@Bean方法中的参数值是通过Spring的依赖注入机制来解决的,这通常涉及到基于类型或名称的自动装配,以及方法参数上的注解。

bean的名称

  • 每个bean都有一个名称,那使用@Bean注解产生的bean在容器中的bean的名称什么,在下面有两个DataSource类型的bean
@Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}/*** 这里bean没有指定名称那这个bean在容器中的名称就 是方法名 "slaveDataSource"* @param druidProperties* @return*/@Bean@ConfigurationProperties("spring.datasource.druid.slave")@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")public DataSource slaveDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}

在@bean注解中有个name参数,根据描述我们可以看出name值就是这个bean的名称,其中If left unspecified, the name of the bean is the name of the annotated method,表示如果没有指定,那这个bean的名称就是@Bean注解所注释的方法的名称,所以上面两个bean的名称分别是masterDataSource 和 slaveDataSource
在这里插入图片描述

  • 如果指定了默认名称,那么这个bean在容器里就叫dynamicDataSource
    @Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource(DataSource masterDataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(masterDataSource, targetDataSources);}

那么参数中 public DynamicDataSource dataSource(DataSource masterDataSource)中的形参masterDataSource来自哪里呢,实际上它是spring从容器中找一个类型为DataSource,名为 masterDataSource的bean,如果把这里改成下面这样就会报错,因为容器中现在有两个DataSource类型的bean,masterDataSource 和 slaveDataSource,这里形参名叫做dataSource,spring根据名称找不到,根据类型能找到两个,不知道注入哪一个,就会报错

@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource1(DataSource dataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), dataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(dataSource, targetDataSources);}

这个时候@Qualifier注解就可以使用了,可以用@Qualifier注解指定bean,将masterDataSource的值 赋值到形参 dataSource

 /**** 这里的形参 dataSource指的容器中DataSource类型的 名为dataSource 的 bean* 但是这里面容器里面没有这个bean,就可以用@Qualifier注解指定bean,将masterDataSource的值 赋值到形参 dataSource*/@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource1(@Qualifier("masterDataSource") DataSource dataSource){Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), dataSource);setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(dataSource, targetDataSources);}

这里实际上还有个@Primary注解,假如有多个相同类型的bean,可以使用@Primary来标明优先用那个bean,但是同一种类型的bean,只能有一个使用@Primary注解,实际上DynamicDataSource是DataSource的子类,所以实际上它们是同一种bean,所以只能有一个@Primary注解

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

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

相关文章

简单仿写SpringIOC

gitee地址&#xff08;需要自取&#xff09;ioc_Imitation: 简单仿写IOC (gitee.com) 项目目录结构 Autowired Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface Autowired { }Component Target(ElementType.TYPE) Retention(RetentionPoli…

文献笔记|综述|When Large Language Model Meets Optimization

When Large Language Model Meets Optimization 题目&#xff1a;当大型语言模型遇到优化时 作者&#xff1a;Sen Huang , Kaixiang Yang , Sheng Qi and Rui Wang 来源&#xff1a;arXiv 单位&#xff1a;华南理工大学 文章目录 When Large Language Model Meets Optimization…

Redis主从部署

文章目录 Redis主从部署1.下载安装Redis2.单点双副本主从配置1.修改配置信息2.修改配置文件redis.conf3.拷贝配置文件到每一个实例文件夹里4.修改每一个实例的端口和工作目录5.配置主从关系6.检查效果 3.哨兵模式监控主从1.创建实例目录2.复制配置文件并进行修改3.启动并测试 4…

Java增加线程后kafka仍然消费很慢

文章目录 一、问题分析二、控制kafka消费速度属性三、案例描述 一、问题分析 Java增加线程通常是为了提高程序的并发处理能力&#xff0c;但如果Kafka仍然消费很慢&#xff0c;可能的原因有&#xff1a; 网络延迟较大&#xff1a;如果网络延迟较大&#xff0c;即使开启了多线…

使用redis进行短信登录验证(验证码打印在控制台)

使用redis进行短信登录验证 一、流程1. 总体流程图2. 流程文字讲解&#xff1a;3.代码3.1 UserServiceImpl&#xff1a;&#xff08;难点&#xff09;3.2 拦截器LoginInterceptor&#xff1a;3.3 拦截器配置类&#xff1a; 4 功能实现&#xff0c;成功存入redis &#xff08;黑…

悠律凝声环Ringbuds Pro耳机:素皮纹理质感独一档,音质也拉满

悠律&#xff08;UMELODY&#xff09;推出的这款新品——凝声环开放式耳机&#xff0c;以其独特的设计风格和出色的音质表现赢得了众多消费者的喜爱。 在外观上&#xff0c;凝声环采用了时尚潮酷的设计理念&#xff0c;并且采用简约典雅素皮工艺&#xff0c;首次将“素皮”材料…

QT文件生成可执行的exe程序

将qt项目生成可执行的exe程序可按照以下步骤进行&#xff1a; 1、在qt中构建运行生成.exe文件&#xff1b; 2、从自定义的路径中取出exe文件放在一个单独的空文件夹中&#xff08;exe文件在该文件夹中的release文件夹中&#xff09;&#xff1b; 3、从开始程序中搜索qt&#xf…

提升Selenium在Chrome上的HTML5视频捕获效果的五个方法

在使用Selenium进行网页自动化测试时&#xff0c;捕获HTML5视频是一个常见的需求。然而&#xff0c;许多开发者发现&#xff0c;在使用Chrome浏览器时&#xff0c;视频捕获效果并不理想&#xff0c;经常出现视频背景为空白的问题。本文将概述五种方法&#xff0c;帮助提升Selen…

品牌渠道低价管控的思考与策略

消费者在购买产品时追求低价、货比三家&#xff0c;这无可非议。然而&#xff0c;产品低价就一定是好事吗&#xff1f;倘若品牌为了迎合消费者对低价的需求&#xff0c;一味追求低价引流&#xff0c;最终的结果只能是从源头压价&#xff0c;比如在原材料的选购、供应商的选择上…

校园运动用品租借租赁租体育用品平台设计

校园运动用品租借租赁租体育用品平台设计 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 基于web的校园运动用品租借平台主要有管理员和用户两个角色功能模块&#xff0c;管理员对后台对有相…

Python 接口自动化中,如何实现参数化测试?

在Python接口自动化中&#xff0c;参数化测试是一种非常重要的技术&#xff0c;它可以帮助我们更高效地执行大量的接口测试用例。参数化测试允许我们通过修改测试数据&#xff0c;多次执行同一个测试用例&#xff0c;以验证系统在不同输入情况下的行为。下面&#xff0c;我将从…

[高频 SQL 50 题(基础版)]第一千七百五十七题,可回收且低脂产品

题目&#xff1a; 表&#xff1a;Products ---------------------- | Column Name | Type | ---------------------- | product_id | int | | low_fats | enum | | recyclable | enum | ---------------------- product_id 是该表的主键&#xff08;具有唯…

php简单商城小程序系统源码

&#x1f6cd;️【简单商城小程序】&#x1f6cd;️ &#x1f680;一键开启&#xff0c;商城搭建新体验&#x1f680; 你还在为繁琐的商城搭建流程头疼吗&#xff1f;现在&#xff0c;有了简单商城系统小程序&#xff0c;一切变得轻松又快捷&#xff01;无需复杂的编程知识&a…

羊大师:暑期不“胖”秘籍:羊奶滋养,细嚼慢咽是关键!

夏日炎炎&#xff0c;假期悠长&#xff0c;如何在享受悠闲时光的同时&#xff0c;保持轻盈体态&#xff0c;成了许多人心中的小秘密。今天&#xff0c;就让我们一起揭秘暑期不“胖”的秘籍&#xff0c;让羊奶的滋养与细嚼慢咽的智慧&#xff0c;成为你美丽夏日的守护神。 羊奶轻…

BUUCTF[堆][of_by_one]

堆中of_by_one 介绍&#xff1a; 严格来说 off-by-one 漏洞是一种特殊的溢出漏洞&#xff0c;off-by-one 指程序向缓冲区中写入时&#xff0c;写入的字节数超过了这个缓冲区本身所申请的字节数并且只越界了一个字节。溢出字节为可控制任意字节 &#xff1a;通过修改大小(size…

一个项目学习Vue3---事件处理

学习下面代码&#xff0c;了解Vue3的事件处理 <!--条件和列表渲染--> <template><el-button v-on:click"countAdd" type"primary">count{{ count }}</el-button><el-button click"countAdd" type"primary"…

18.按键消抖模块设计(使用状态机,独热码编码)

&#xff08;1&#xff09;设计意义&#xff1a;按键消抖主要针对的时机械弹性开关&#xff0c;当机械触点断开、闭合时&#xff0c;由于机械触点的弹性作用&#xff0c;一个按键开关在闭合时不会马上稳定地接通&#xff0c;在断开时也不会一下子就断开。因而在闭合以及断开的瞬…

解决centos yum和wget指令都用不了(换源)

先ping一下网络&#xff0c;看看能不能ping通&#xff0c;先排除是网络的问题 ping www.baidu.com有消息回传说明网络有连接&#xff0c;没有的话就要去把虚拟机的网络连接好&#xff08;CtrlC关闭&#xff09; 接下来写centos更换源 一般yum用不了&#xff0c;下载不来wget …

树莓派采集系统

树莓派&#xff08;Raspberry Pi&#xff09;是一款非常受欢迎的小型单板计算机&#xff0c;因其低成本、低功耗以及丰富的I/O接口&#xff0c;非常适合用来搭建数据采集系统。无论是环境监测、智能家居、工业自动化&#xff0c;还是科学实验&#xff0c;树莓派都能胜任。以下是…

苹果开发者取消自动续费

文档&#xff1a;https://support.apple.com/zh-cn/118428 如果没有找到订阅&#xff0c;那就是账号不对 取消订阅后&#xff0c;就不会自动续费了&#xff0c;如果不放心&#xff0c;可以把付款绑定的方式也取消