Spring Boot 中 Bean 的机制详解

news2024/10/12 3:21:42

Spring Boot 的魔力在于其自动配置和 Bean 管理,它极大地简化了 Spring 应用的开发。本文将结合之前的内容,更加全面、深入地解释 Spring Boot 如何管理 Bean、自动装配的底层原理以及相关的使用细节,并提供更丰富的示例。

1. Bean 管理核心机制

Spring 容器是 Bean 的生命周期管理中心,负责 Bean 的创建、配置、组装和销毁。Spring Boot 通过自动化配置简化了 Bean 的管理过程,开发者只需少量配置即可拥有一个功能完善的 Spring 应用。

  • @SpringBootApplication: 这个注解是 Spring Boot 的核心,它整合了三个关键注解:

    • @SpringBootConfiguration: 标明该类是一个 Spring Boot 配置类,等同于 @Configuration,允许在该类中定义 Bean。

    • @EnableAutoConfiguration: 启用 Spring Boot 的自动配置机制。它利用 Spring Factories 机制加载 META-INF/spring.factories 文件中定义的自动配置类,根据 classpath 中的依赖自动配置 Bean,例如数据库连接、Web 服务器等。

    • @ComponentScan: 自动扫描指定包及其子包,查找带有 @Component、@Service、@Repository、@Controller 等 stereotype 注解的类,并将它们注册为 Spring Bean。可以自定义扫描的包路径。

  • Bean 定义: Bean 定义是 Spring 容器创建 Bean 的蓝图,包含了 Bean 的所有信息,例如类名、作用域、初始化方法、销毁方法、依赖关系等。

  • Bean 的生命周期:

    • 实例化: Spring 容器根据 Bean 定义创建 Bean 实例,可以使用构造器注入、工厂方法等方式创建。

    • 属性赋值 (Populate): 注入 Bean 的依赖和其他属性值,可以使用 setter 注入、字段注入等方式。

    • Bean 后置处理器 (BeanPostProcessor): BeanPostProcessor 接口允许开发者在 Bean 初始化前后执行自定义逻辑。Spring 内置了许多 BeanPostProcessor,例如 AutowiredAnnotationBeanPostProcessor 负责处理 @Autowired 注解。

      • Aware 接口: Spring 提供了一系列 Aware 接口,例如 BeanFactoryAware、ApplicationContextAware 等,允许 Bean 获取 Spring 容器相关的对象。

    • 初始化 (Initialize): 调用初始化方法,例如 @PostConstruct 注解的方法、InitializingBean 接口的 afterPropertiesSet() 方法或 XML 配置中的 init-method。

    • 使用: Bean 可以被应用程序使用。

    • 销毁 (Destroy): 调用销毁方法,例如 @PreDestroy 注解的方法、DisposableBean 接口的 destroy() 方法或 XML 配置中的 destroy-method。

2. 自动装配核心原理

Spring Boot 的自动装配机制是其魔法所在,能够根据 classpath 中的依赖自动配置 Bean,大大减少了配置工作。

  • @EnableAutoConfiguration 深入解读: 该注解会触发 Spring Boot 的自动配置机制,它导入 AutoConfigurationImportSelector,该选择器利用 Spring Factories 机制加载 META-INF/spring.factories 文件中定义的自动配置类。

  • Spring Factories 机制详解: META-INF/spring.factories 文件是一个 key-value 映射文件,key 是接口的全限定名,value 是实现类的全限定名列表。Spring Boot 通过读取这个文件,加载所有自动配置类。

  • 条件化注解精细控制: 自动配置类通常使用条件化注解,例如:

    • @ConditionalOnClass:当 classpath 中存在指定的类时生效。

    • @ConditionalOnMissingClass:当 classpath 中不存在指定的类时生效。

    • @ConditionalOnBean:当 Spring 容器中存在指定的 Bean 时生效。

    • @ConditionalOnMissingBean:当 Spring 容器中不存在指定的 Bean 时生效。

    • @ConditionalOnProperty:当指定的配置属性存在且值为 true 时生效。

  • 自动装配流程 (更详细):

    • Spring Boot 启动时,AutoConfigurationImportSelector 读取 META-INF/spring.factories 文件,获取所有自动配置类。

    • 对于每个自动配置类,Spring 容器会检查其条件化注解,判断是否满足条件。

    • 如果满足条件,则该自动配置类生效,其中定义的 @Bean 方法会被调用,创建 Bean 并注册到 Spring 容器中。

    • AutowiredAnnotationBeanPostProcessor 会处理 @Autowired 注解,根据类型、名称或 qualifier 查找匹配的 Bean,并进行注入。

3. 获取 Bean 的场景化应用

  • @Autowired (推荐): 简洁方便,适用于大多数场景。

  • @Resource: 灵活,可以根据名称或类型注入。

  • ApplicationContext: 功能强大,可以获取任何 Bean,但代码略显繁琐。 适用于需要动态获取 Bean 的场景。

4. Bean 作用域

在Spring中支持五种作用域,后三种在web环境才生效:

作用域说明
singleton容器内同名称的bean只有一个实例(单例)(默认)
prototype每次使用该bean时会创建新的实例(非单例)
request每个请求范围内会创建新的实例(web环境中)
session每个会话范围内会创建新的实例(web环境中)
application每个应用范围内会创建新的实例(web环境中)

5. 第三方 Bean 集成策略

  • @Bean 注解: 最常用的方式,灵活控制 Bean 的创建过程。

  • @Import 注解: 方便导入其他配置类。

  • 手动注册 BeanDefinition: 更高级的用法,可以动态注册 Bean。

6. 示例

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}


@Component
class MyBean {
    private String message = "Hello from MyBean";

    public String getMessage() {
        return message;
    }
}



@Service
class MyService {

    private final MyBean myBean;
    private final ApplicationContext context;

    @Autowired
    public MyService(MyBean myBean, ApplicationContext context) {
        this.myBean = myBean;
        this.context = context;
    }

    public void doSomething() {
        System.out.println(myBean.getMessage());
        MyBean anotherBean = context.getBean(MyBean.class);
        System.out.println(anotherBean.getMessage());  //获取同一个bean
    }
}


@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, 
       proxyMode = ScopedProxyMode.TARGET_CLASS)
class PrototypeBean {
    private int counter = 0;

    public int getCounter() {
        return ++counter;
    }
}


@Service
class AnotherService {

    @Autowired
    private PrototypeBean prototypeBean; // 注入代理对象

    public void testPrototype(){
        System.out.println("Prototype Bean 1: " + prototypeBean.getCounter());
        System.out.println("Prototype Bean 2: " + prototypeBean.getCounter());  // 每次调用都会增加
    }
}



@RestController
class MyController {

    private final MyService service;
    private final AnotherService anotherService;


    @Autowired
    public MyController(MyService service, AnotherService anotherService) {
        this.service = service;
        this.anotherService = anotherService;
        service.doSomething();
        anotherService.testPrototype();
    }

    @GetMapping("/")
    public String hello() {
        anotherService.testPrototype();//每次调用都会生成新的prototype bean
        return "Hello from Spring Boot!";
    }
}

代码解释:(以上代码为了解释方便就写在了一起,仅供参考)

  • PrototypeBean:

    • @Scope 注解指定了 prototype 作用域,确保每次请求都会创建一个新的实例。

    • proxyMode = ScopedProxyMode.TARGET_CLASS 至关重要。它创建了一个 CGLIB 代理来包装 PrototypeBean。 因为 AnotherService 是 singleton 的,它只会在启动时注入一次 PrototypeBean。如果没有代理,注入的就是启动时创建的那个 PrototypeBean 实例,之后每次调用 getCounter() 都是同一个实例。 使用代理后,每次调用 prototypeBean.getCounter() 时,代理都会从 Spring 容器获取一个新的 PrototypeBean 实例,从而实现了预期的行为.

  • AnotherService:

    • @Autowired 注入的 prototypeBean 现在是代理对象。

  • MyController:

    • 注入了 AnotherService 并在构造函数和 hello() 方法中调用了 testPrototype() 方法,演示了代理的效果. 在 hello() 方法中每次调用都会生成新的 prototype bean.

现在,运行应用并多次访问 / 端点,你会看到 PrototypeBean 的计数器每次都会增加,证明每次都注入了新的实例. 这体现了代理模式在处理不同作用域 Bean 注入时的重要作用.

这个完整的示例演示了 prototype 作用域的正确用法,并解释了代理模式如何解决在 singleton bean 中注入 prototype bean 的问题。 这对于理解 Spring Bean 的作用域和依赖注入至关重要。

总结:

本文更加深入地解释了 Spring Boot 的 Bean 管理和自动装配机制,并提供了更丰富的示例和最佳实践。 理解这些核心概念和细节,可以更好地利用 Spring Boot 构建更强大、更灵活的应用程序。 建议深入学习 Spring Boot 的文档和源码,以掌握更多高级特性。希望对各位看官有所帮助,感谢各位看官的观看,谢谢,下期见~

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

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

相关文章

聚类分析 | IPOA优化FCM模糊C均值聚类优化算法

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (多图聚类)IPOA优化FCM模糊C均值聚类优化算法,matlab代码,超多图 基于改进的鹈鹕优化算法(IPOA)优化FCM模糊C均值聚类优化,matlab代码,直接运行…

万字长文解读预测市场的历史发展与未来前景

撰文:Alex Nardi,Shoal Research 编译:Yangz,Techub News 文章要点: 预测市场的发展和影响:预测市场利用集体智慧预测未来事件的能力日益得到认可,对政治、金融、娱乐和流行文化等领域产生了重…

HiRT | 异步控制策略,告别VLA时延问题

论文:HiRT: Enhancing Robotic Control with Hierarchical Robot Transformers 前言:HiRT 通过异步处理的策略,将 VLM 作为低频慢思考过程,将轻量的动作策略模型作为高频快响应过程 ,以此解决 VLA 驱动带来的控制时延问…

Ubuntu22.04环境下源码安装OpenCV 4.8.1

因为项目需要用OpenCV对yolov8模型进行推理,通过DNN模块,之前本地的OpenCV版本是4.5.4(好像安装完ROS2 humble之后系统就自带了opencv),加载onnx模型一直报错,网上查询到需要4.7以上,干脆直接升…

vue3之依赖注入provide(提供)/inject(注入)

通常情况下,需要从父组件传值到子组件使用props足以,但是如果是深层嵌套的组件,如果某个深层的组件想要得到祖先组件的部分数据,使用props的话需要沿着各个嵌套的组件着层传递数据,而在传递过程中的组件压根就不需要使…

你了解 SpringBoot 在一次 http 请求中耗费了多少内存吗?

在实际工作中,经常会需要进行在全链路压测,优化 GC参数,优化 JVM 内存分配。 当知道 1 次 RPC 请求和 Http 请求需要的堆内存大小后,你可以精确地计算:指定的并发量之下,系统需申请多少堆内存。同时结合 J…

若依 根据角色权限 动态添加路由 学习

源于这个问题对若依权限改造的学习,用ASP.NET Core Web api 做后端,所以不是纯净的若依前端,有部分改过。 ​​​​​​​若依 从字典类型跳到字典数据跳到了404-CSDN博客 从路由守卫获取到用户信息开始,到路由跳转结束的过程 …

小猿搜题冲榜/刷排名/专用思路-理论速度1小时/3.6w分 附带0s教程

小猿搜题冲榜/刷排名/专用思路-理论速度1小时/3.6w分 附带0s教程 ⚠️:这个方法很多还需要手动操作,我目前无法用代码完全实现,如果你有兴趣可以给我提issue我们一起讨论。 冲榜思路 先说整体思路:抓包改答案adb模拟 之后详细…

10月更新|国内可用的ChatGPT攻略镜像中文网站

一、GPT中文镜像站 ① yixiaai.com 支持GPT4、4o以及o1,支持MJ绘画 ② chat.lify.vip 支持通用全模型,支持文件读取、插件、绘画、AIPPT ③ AI Chat支持GPT3.5/4,4o以及MJ绘画 1. 什么是镜像站 镜像站(Mirror Site&#xff0…

【GO基础学习】环境安装到基础语法(1)

文章目录 环境安装GoLand 安装GO基础GO特点类型和函数Init函数和main函数GO命令下划线变量和常量数组切片Slice 引用 环境安装 下载地址:https://www.golangroadmap.com/ 安装目录文件说明: api:每个版本的 api 变更差异。 bin&#xff1…

JAVA 字符串 trim() 方法的正确使用

JAVA类里面 trim() 方法大家都比较熟悉,就是用来清除掉字符串首尾的空白字符。但在一次程序运行崩溃后,查找具体原因时,发现是由字符串末尾的一个回车符号 "\r" 所导致的。于是有机会仔细读了下该方法的 java 文档说明。其中一段内…

Docker理念

1.为什么会出现Docker Docker 的出现并非偶然,而是由一系列技术发展趋势和实际需求所推动的一项技术创新。 随着软件行业的快速发展,开发团队的规模不断扩大,成员可能分布在不同的地理位置,使用不同的操作系统和开发工具。这就导致…

CSD(computational storage devices)架构介绍

CSD(computational storage devices)架构介绍 前言一、CSD与传统SSD的架构对比二、为什么要采用FPGA三、FPGA缺点四、个人总结reference 前言 虽然一直有接触CSD,但一直对其原理和架构知之甚少,半知不解。今天,趁着我还…

element-ui点击文字查看图片预览功能

今天做一个点击文字查看图片的功能&#xff0c;大体页面长这样子&#xff0c;点击查看显示对应的图片 引入el-image-viewer&#xff0c;点击的文字时候设置图片预览组件显示并传入图片的地址 关键代码 <el-link v-if"scope.row.fileList.length > 0" type&…

模型预测控制工具包——ACADO:简介、安装与测试

模型预测控制工具包——ACADO&#xff1a;简介、安装与测试 ACADO 工具包简介ubuntu20.04 安装 ACADO工具包安装依赖安装ACADOtoolkit 测试 ACADO 工具包简介 ACADO Toolkit 是一个用 C 编写的用于自动控制和动态优化的软件环境和算法集合。 它提供了一个通用框架&#xff0c;…

三菱FX3UPLC定位控制程序举例

测试程序的编写 1.输入输出的分配输入输出的分配如下表所示。 2、相关软元件的设定 相关软元件也有所不同。更改定位指令的脉冲输出端时&#xff0c;根因设定为定位指令的脉冲输出端的软元件不同&#xff0c;据更改的内容&#xff0c;需要变更设定的相关软元件。 3.程…

【大模型新书】掌握大语言模型:高级技术、应用、尖端方法和顶尖LLMs

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/93e5a5c008474f72a0335083ef9c4893.png)我已将 这本大模型书籍免费分享 出来&#xff0c;需要的小伙伴可以扫取。 主要特性 探索自然语言处理&#xff08;NLP&#xff09;基础和大语言模型基本原理&#xff0c;包括…

若依前端后打成一个JAR包部署

客户需要将项目前后端作为一个整体打包成jar&#xff0c;不使用nginx方式转发。使用框架是若依前后端分离&#xff0c;后端springboot&#xff0c;前端vue&#xff0c;目的就是把vue打入jar。 一、前端修改 ruoyi-ui/src/router/index.js文件 &#xff0c;将 mode: ‘history’…

一键生成二维码的源码系统 电脑+手机版自适应代码 带完整的安装代码包以及搭建部署教程

系统概述 一键生成二维码的源码系统是一款集二维码生成、管理和应用于一体的综合性平台。它采用先进的技术和算法&#xff0c;能够快速、准确地生成各种类型的二维码&#xff0c;包括文本、链接、图片等。同时&#xff0c;该系统还具备高度的灵活性和可扩展性&#xff0c;能够…

如何使用bpmn-js实现可视化流程管理

介绍 BPMN-JS是一个流行的开源库&#xff0c;用于在Web应用程序中可视化、创建、编辑和分析BPMN&#xff08;Business Process Model and Notation&#xff0c;业务流程建模与表示法&#xff09;2.0 图。BPMN是一种国际标准的图形化语言&#xff0c;用于描述企业中的业务流程&a…