跳到主要内容Spring Bean 管理与 Spring Boot 自动配置原理 | 极客日志Javajava
Spring Bean 管理与 Spring Boot 自动配置原理
Spring Bean 管理包含作用域与生命周期机制。作用域如 singleton、prototype 等决定实例化范围。生命周期涵盖实例化、注入、初始化、使用和销毁。Spring Boot 自动配置通过@EnableXxxx 注解封装@Import,结合 ImportSelector 接口实现依赖的自动化加载,减少手动配置,简化开发流程。
蜜桃汽水14 浏览 Spring Bean 管理与 Spring Boot 自动配置原理
1. Bean 的作用域和生命周期
1.1 Bean 的作用域
在 Spring 中,Bean 的作用域(Scope)决定了 Bean 的实例化方式以及其生命周期。以下是 Spring 中常见的 Bean 作用域:
| 作用域 | 说明 |
|---|
| singleton | 每个 Spring IoC 容器内同名称的 bean 只有一个实例 (单例) (默认) |
| prototype | 每次使用该 bean 时会创建新的实例 (非单例) |
| request | 每个 HTTP 请求生命周期内,创建新的实例 |
| session | 每个 HTTP Session 生命周期内,创建新的实例 |
| application | 每个 ServletContext 生命周期内,创建新的实例 |
| websocket | 每个 WebSocket 生命周期内,创建新的实例 |
测试不同作用域的 Bean 取到的对象是否一样:
创建一个 Dog 实体类:
public class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建一个 DogComponent 类(交给 Spring 进行管理):
@Component
public class DogComponent {
@Bean
public Dog dog() {
return new Dog();
}
@Bean
public Dog singleDog() {
();
}
Dog {
();
}
Dog {
();
}
Dog {
();
}
Dog {
();
}
}
return
new
Dog
@Bean
@Scope("prototype")
public
prototypeDog
()
return
new
Dog
@Bean
@RequestScope
public
requestDog
()
return
new
Dog
@Bean
@SessionScope
public
sessionDog
()
return
new
Dog
@Bean
@ApplicationScope
public
applicationDog
()
return
new
Dog
@RestController
@RequestMapping("/dog")
public class DogController {
@Autowired
ApplicationContext context;
@Resource(name = "singleDog")
Dog singleDog;
@Resource(name = "prototypeDog")
Dog prototypeDog;
@RequestMapping("/singleton")
public String singleton() {
Dog contextDog = context.getBean("singleDog", Dog.class);
return "contextDog" + contextDog + ",resource" + singleDog;
}
@RequestMapping("/prototype")
public String prototype() {
Dog contextDog = context.getBean("prototypeDog", Dog.class);
return "contextDog" + contextDog + ",resource" + prototypeDog;
}
@RequestMapping("/request")
public String request() {
Dog contextDog = context.getBean("requestDog", Dog.class);
return "contextDog" + contextDog + ",resource" + requestDog;
}
@RequestMapping("/session")
public String session() {
Dog contextDog = context.getBean("sessionDog", Dog.class);
return "contextDog" + contextDog + ",resource" + sessionDog;
}
@RequestMapping("/application")
public String application() {
Dog contextDog = context.getBean("applicationDog", Dog.class);
return "contextDog" + contextDog + ",resource" + applicationDog;
}
}
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
- Singleton(单例)
- Spring 默认的作用域,所有客户端共享同一个 Bean 实例。
- 适用于无状态 Bean。
多次访问,得到的都是同一个对象,并且 @Autowired 和 applicationContext.getBean() 也是同一个对象。
- Prototype(原型)
- 每次注入或获取 Bean 时,都会创建一个新的实例。
- 适用于有状态 Bean。
观察 ContextDog,每次获取的对象都不一样 (注入的对象在 Spring 容器启动时,就已经注入了,所以多次请求也不会发生变化)。
- Request(请求范围)
- 每个 HTTP 请求创建一个新的 Bean 实例。
- 适用于 Web 应用中的请求相关数据。
在一次请求中,@Autowired 和 applicationContext.getBean() 也是同一个对象。但是每次请求,都会重新创建对象。
- Session(会话范围)
- 每个 HTTP 会话创建一个新的 Bean 实例。
- 适用于用户会话相关数据。
在一个 session 当中,多次请求,获取的对象都是同一个。但是我们换一个浏览器访问会重新创建对象(另外一个 session)。
- Application(应用范围)
- 每个 ServletContext 创建一个 Bean 实例。
在一个应用中,多次访问都是同一个对象。Application scope 就是对于整个 web 容器来说,bean 的作用域是 ServletContext 级别的。这个和 singleton 有点类似,区别在于:Application scope 是 ServletContext 的单例,singleton 是一个 ApplicationContext 的单例。在一个 web 容器中 ApplicationContext 可以有多个。
1.2 Bean 的生命周期
Bean 的生命周期从创建到销毁,Spring 对其进行了详细的管理:
- Bean 的创建
- 依赖注入
- 初始化回调
- 调用 Bean 的初始化方法(如
@PostConstruct 注解标注的方法)。
- Spring 的
InitializingBean 接口或自定义初始化方法。
- Bean 可用
- 销毁回调
- 当应用上下文关闭时,调用 Bean 的销毁方法。
- 如
@PreDestroy 注解标注的方法,或 DisposableBean 接口。
创建一个 BeanLifeComponent 类继承 BeanNameAware 来说明 Bean 的生命周期从创建到销毁。
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BeanLifeComponent implements BeanNameAware {
public BeanLifeComponent() {
System.out.println("执行构造方法....");
}
@Autowired
public void setDogCompoent(DogComponent dogCompoent) {
this.dogCompoent = dogCompoent;
System.out.println("执行属性赋值");
}
@Override
public void setBeanName(String name) {
System.out.println("执行 BeanNameAware,beanName:" + name);
}
@PostConstruct
public void init() {
System.out.println("初始化方法...");
}
public void use() {
System.out.println("使用 Bean,执行 use 方法");
}
@PreDestroy
public void destroy() {
System.out.println("销毁 bean");
}
}
@Test
void testBean() {
BeanLifeComponent bean = context.getBean(BeanLifeComponent.class);
bean.use();
}
- 实例化
- 属性赋值
- 设置 Bean 名称
- 初始化
- 使用 Bean
- 销毁 Bean
2. Spring Boot 自动配置流程
SpringBoot 的自动配置就是当 Spring 容器启动后,一些配置类、bean 对象等就自动存到了 IoC 容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
Spring Boot 自动配置,就是指 Spring Boot 是如何将依赖 jar 包中的配置类以及 Bean 加载到 Spring IoC 容器中的。以下是其核心流程:
数据准备
import org.springframework.context.annotation.Configuration;
@Configuration
public class Sliqverconfig {
public void study() {
System.out.println("Sliqverconfig study... ");
}
}
获取 Sliqverconfig 这个 Bean,写测试用例:
@Autowired
ApplicationContext context;
@Test
void testBean() {
Sliqverconfig bean = context.getBean(Sliqverconfig.class);
System.out.println(bean);
}
原因分析
Spring 通过五大注解和 @Bean 注解可以帮助我们把 Bean 加载到 SpringIoC 容器中,以上有个前提就是这些注解类需要和 SpringBoot 启动类在同一个目录下 (@SpringBootApplication 标注的类 就是 SpringBoot 项目的启动类)。
可以看到这个 Sliqverconfig 类并不和启动类在同一个包下面。这个 Sliqverconfig 类相当于第三方包,那我们怎么样把这个包,交给 Spring 管理这些 Bean 呢?
解决方案
我们需要指定路径或者引入的文件,告诉 Spring,让 Spring 进行扫描到。常见的解决方法有两种:
1. @ComponentScan 组件扫描
@ComponentScan(basePackages = "com.config")
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
2. @Import
@Import(Sliqverconfig.class)
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
3. 导入 ImportSelector 接口实现类
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.config.Sliqverconfig"};
}
}
@Import(MySelector.class)
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
问题:
但是他们都有一个明显的问题,就是使用者需要知道第三方依赖中有哪些 Bean 对象或配置类。如果漏掉其中一些 Bean,很可能导致我们的项目出现大的事故。这对程序员来说非常不友好。
依赖中有哪些 Bean,使用时候需要配置哪些 bean,第三方依赖最清楚,那能否由第三方依赖来做这件事呢?
比较常见的方法就是第三方依赖给我们提供一个注解,这个注解一般都以@EnableXxxx 开头的注解,注解中封装的就是 @Import 注解。
第三方依赖提供注解
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MySelector.class)
public @interface EnableSliqversConfig {}
注解中封装 @Import 注解,导入 MySelector.class。
@EnableSliqversConfig
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
3. 总结
Spring 的 Bean 管理和生命周期机制是其核心功能,而 Spring Boot 的自动配置流程则大大简化了配置工作,帮助开发者快速构建应用。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online