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://xiahunao.cn/news/3223984.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

使用微pe装系统

本文仅作为记录&#xff0c;不作为教程。 今天心血来潮想下点游戏玩玩&#xff0c;一看之前分的200gc盘已经红了&#xff0c;再加上大学之后这个笔记本已经用得很少了&#xff0c;于是打算重装电脑。 参考: 微PE辅助安装_哔哩哔哩_bilibil… 1.下载微pe和win10系统到U盘 我这…

Day65 代码随想录打卡|回溯算法篇---组合总和II

题目&#xff08;leecode T40&#xff09;&#xff1a; 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含…

JAVA的String的不可变特性

在学习JAVA的时候&#xff0c;看到了JAVA的String具有不可变的特性&#xff0c;他是说&#xff0c;JAVA的String在创建好后&#xff0c;JVM将这个String变量指向内存中的一个地址&#xff0c;当下次改变这个String变量的时候&#xff0c;改变的不是这个变量的值&#xff0c;而是…

可转债之强赎条款

摘要&#xff1a;每天学习一点金融小知识 做可转债投资&#xff0c;强赎风险是特别需要注意的&#xff0c;若投资者没有及时采取措施&#xff0c;就有可能造成很大的损失。本文从可转债的定义、强赎条款的原因及强赎的情况几个方面来介绍下可转债的强赎条款。 什么是可转换债券…

如何评价Flutter?

哈喽&#xff0c;我是老刘 我们团队使用Flutter已经快6年了。 有很多人问过我们对Flutter的评价。 今天在这里回顾一下6年前选择Flutter时的原因&#xff0c;以及Flutter在这几年中的实际表现如何。 选择Flutter时的判断 1、性能 最开始吸引我们的就是其优秀的性能。 特别是…

imx6ull/linux应用编程学习(15) 移植MQTT客户端库

1. 准备开发环境 确保你的Ubuntu系统已经安装了必要的工具和依赖项。打开终端并运行以下命令&#xff1a; sudo apt update sudo apt install build-essential cmake git2. 获取MQTT库 git clone https://github.com/eclipse/paho.mqtt.c.git cd paho.mqtt.c3. 编译MQTT库 mk…

FullCalendar的使用,react日历组件

1.下载 yarn add fullcalendar/core fullcalendar/react fullcalendar/daygrid 2.运行 import React from react; import FullCalendar from "fullcalendar/react"; import dayGridPlugin from "fullcalendar/daygrid";const ExperimentalSchedule () …

昇思25天学习打卡营第10天|应用实践之基于MindNLP和ChatGLM-6B实现一个聊天应用

基本介绍 今天的应用实践是基于MindSpore和ChatGLM-6B实现一个&#xff08;伪&#xff09;聊天应用&#xff0c;本质上就是使用MindSpore下载模型及其权重&#xff0c;然后调用相关API输入自己想说的话&#xff0c;就可以得到回复&#xff0c;如果要打造真正的聊天应用&#xf…

中文大模型基准测评2024上半年报告

中文大模型基准测评2024上半年报告 原创 SuperCLUE CLUE中文语言理解测评基准 2024年07月09日 18:09 浙江 SuperCLUE团队 2024/07 背景 自2023年以来&#xff0c;AI大模型在全球范围内掀起了有史以来规模最大的人工智能浪潮。进入2024年&#xff0c;全球大模型竞争态势日益加…

对比学习和多模态任务

1. 对比学习 对比学习&#xff08;Contrastive Learning&#xff09;是一种自监督学习的方法&#xff0c;旨在通过比较数据表示空间中的不同样本来学习有用的特征表示。其核心思想是通过最大化同类样本之间的相似性&#xff08;或降低它们之间的距离&#xff09;&#xff0c;同…

科普文本分类背后的数学原理——最新版《数学之美》第14、15章读书笔记

新闻分类&#xff0c;或广义上的文本分类&#xff0c;其核心任务是根据文本内容将相似文本聚合在同一类别中。在新闻领域&#xff0c;这意味着将报道划分为财经、体育、军事等不同主题。人类执行此任务时&#xff0c;通过阅读和理解新闻的主旨来进行归类。然而&#xff0c;作者…

第二章 基础知识(4) - 日志记录

在默认日志级别&#xff0c;Blazor项目中默认提供如下日志记录提供程序&#xff1a; 在服务器上&#xff08;Blazor Server&#xff09;&#xff0c;日志记录仅发生在 LogLevel.Information 或更高级别的 Development 环境中的服务器端 .NET 控制台。 在客户端上&#xff08;B…

泛微E9开发 控制日期浏览按钮的可选日期范围

控制日期浏览按钮的可选日期范围 1、需求说明2、实现方法3、扩展知识点控制日期浏览按钮的可选日期范围格式参数说明演示 1、需求说明 控制日期浏览按钮的可选日期范围为2024/07/01~2024/07/31&#xff0c;如下图所示 2. 控制日期浏览按钮的可选日期范围在当前时间的前一周~当…

生成多个ssh访问不同git

如果&#xff0c;你的git代码仓库&#xff0c;比如说腾讯云coding&#xff0c;通过ssh秘钥访问&#xff0c;一直用的好好的&#xff0c;有一天&#xff0c;你又增加一个aliyun云效的代码仓库&#xff0c;又配置了aliyun云效的秘钥并且&#xff0c;根据aliyun云效的官方文档上传…

Django 更新数据 save()方法

1&#xff0c;添加模型 Test/app11/models.py from django.db import modelsclass Post(models.Model):title models.CharField(max_length200)content models.TextField()pub_date models.DateTimeField(date published)class Book(models.Model):title models.CharFie…

【Linux】多线程_2

文章目录 九、多线程2. 线程的控制 未完待续 九、多线程 2. 线程的控制 主线程退出 等同于 进程退出 等同于 所有线程都退出。为了避免主线程退出&#xff0c;但是新线程并没有执行完自己的任务的问题&#xff0c;主线程同样要跟进程一样等待新线程返回。 pthread_join 函数…

力扣喜刷刷--day1

1.无重复字符的最长子串 知识点&#xff1a;滑动窗口 基本概念 窗口&#xff1a;窗口是一个连续的子序列&#xff0c;可以是固定长度或可变长度。滑动&#xff1a;窗口在数据序列上移动&#xff0c;可以是向左或向右。边界&#xff1a;窗口的起始和结束位置。 应用场景 字符…

基于Python的51job招聘数据采集与可视化项目实践

项目背景与目标 在当今竞争激烈的就业市场中&#xff0c;深入分析招聘信息对于求职者和企业都具有重要意义。基于Python的51job招聘数据采集与可视化项目旨在通过自动化手段高效获取大量招聘信息&#xff0c;并对这些数据进行深度分析和展示。 51job作为中国领先的招聘网站&…

干货:高水平论文写作思路与方法

前言:Hello大家好,我是小哥谈。高水平论文的写作需要扎实的研究基础和严谨的思维方式。同时,良好的写作技巧和时间管理也是成功的关键。本篇文章转载自行业领域专家所写的一篇文章,希望大家阅读后可以能够有所收获。🌈 目录 🚀1.依托事实/证据,通过合理的逻辑,…

【深度学习基础】环境搭建 linux系统下安装pytorch

目录 一、anaconda 安装二、创建pytorch1. 创建pytorch环境&#xff1a;2. 激活环境3. 下载安装pytorch包4. 检查是否安装成功 一、anaconda 安装 具体的安装说明可以参考我的另外一篇文章【环境搭建】Linux报错bash: conda: command not found… 二、创建pytorch 1. 创建py…