Spring Boot 中 Bean 的机制详解

ops/2024/10/18 18:14:14/

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. 示例

java">@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;@Autowiredpublic 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 {@Autowiredprivate 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;@Autowiredpublic MyController(MyService service, AnotherService anotherService) {this.service = service;this.anotherService = anotherService;service.doSomething();anotherService.testPrototype();}@GetMapping("/")public String hello() {anotherService.testPrototype();//每次调用都会生成新的prototype beanreturn "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.ppmy.cn/ops/124257.html

相关文章

绑定Rust变量会踩什么坑

讲动人的故事,写懂人的代码 3.2 变量绑定的声明和初始化分开 在3.1.1中提到,变量的声明和初始化可以分开。而这也为程序员挖了一个坑,如代码清单3-4所示。 本书代码下载链接为github.com/wubin28/book_LRBACP。本书所有的代码清单&#xff…

【DataLoom】智能问数 - 自然语言与数据库交互

探索DataLoom的智能问数功能:简化数据库查询 在数据驱动的决策制定中,数据库查询是获取洞察的关键步骤。但是,传统的数据库查询方法往往复杂且技术性强,这限制了非技术用户的使用。DataLoom的智能问数功能正是为了解决这一问题而…

Orecle 迁移 人大金仓数据库 SQL 问题

1. start with ... connect by 语法不兼容 使用 oracle 项目一般使用,start with ... connect by 语法做菜单栏数据查询,该语法在人大金仓数据库提供的可视化工具中可以执行,但在Springboot mybatis 项目中无法执行(版本2.X),通…

Spring Boot集成encache快速入门Demo

1.什么是encache EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。 Ehcache 特性 优点 快速、简单支持多种缓存策略:LRU、LFU、FIFO 淘汰算法缓存数据有两级:内存和磁盘&a…

以FLV解复用为例详解开源库FFmpeg中解复用器的源码逻辑及处理流程

目录 1、FFmpeg简介 2、FLV文件格式介绍 3、注册解复用器 4、解复用器的处理 4.1、AVFormatContext 4.1.1、AVClass 4.1.2、AVOption 4.1.3 AVDictionary—AV字典 4.1.4、AVIOContext 4.1.4.1、URLProtocol 4.1.4.2、AVIOContext的初始化及获取 4.1.5、AVInputF…

【工具】前端js数字金额转中文大写金额

【工具】前端js数字金额转中文大写金额 代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>金额转…

鸿蒙fork()功能

fork功能 上层通过使用fork()函数创建新进程。 fork是什么&#xff1f; #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h>int main(void) {pid_t pid;char *message;int n;pid fork();if (pid < 0) {perror…

LUCEDA IPKISS Tutorial 77:在版图一定范围内填充dummy

案例分享&#xff1a;在给定的Shape内填充dummy 所有代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3 from shapely.geometry import Polygon, MultiPolygon import numpy as np import matplotlib.pyplot as pltclass CellFilledWithCon…