跳到主要内容Spring Bean 管理与 Spring Boot 自动配置原理 | 极客日志Javajava
Spring Bean 管理与 Spring Boot 自动配置原理
Spring Bean 作用域包括单例、原型、请求、会话及应用范围,分别控制实例的生命周期。Bean 生命周期涵盖实例化、依赖注入、初始化回调及销毁回调。Spring Boot 自动配置通过 @ComponentScan、@Import 及 Enable 注解机制,将第三方依赖中的配置类与 Bean 加载至 IoC 容器,简化开发流程并降低手动配置成本。
山野诗人1 浏览 Spring Bean 管理与生命周期
1. Bean 的作用域
在 Spring 中,Bean 的作用域(Scope)决定了 Bean 的实例化方式以及其生命周期。以下是 Spring 中常见的 Bean 作用域:
| 作用域 | 说明 |
|---|
| singleton | 每个 Spring IoC 容器内同名称的 bean 只有一个实例 (单例) (默认) |
| prototype | 每次使用该 bean 时会创建新的实例 (非单例) |
| request | 每个 HTTP 请求生命周期内,创建新的实例 |
| session | 每个 HTTP Session 生命周期内,创建新的实例 |
| application | 每个 ServletContext 生命周期内,创建新的实例 |
| websocket | 每个 WebSocket 生命周期内,创建新的实例 |
代码示例
创建一个 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() {
return new Dog();
}
Dog {
();
}
Dog {
();
}
Dog {
();
}
Dog {
();
}
}
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- 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
@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() 也是同一个对象。
- 接口:
http://127.0.0.1:8080:/dog/singleton
-
Prototype(原型)
- 每次注入或获取 Bean 时,都会创建一个新的实例。
- 适用于有状态 Bean。
- 观察 ContextDog,每次获取的对象都不一样 (注入的对象在 Spring 容器启动时,就已经注入了,所以多次请求也不会发生变化)。
- 接口:
http://127.0.0.1:8080:/dog/prototype
-
Request(请求范围)
- 每个 HTTP 请求创建一个新的 Bean 实例。
- 适用于 Web 应用中的请求相关数据。
- 在一次请求中,
@Autowired 和 applicationContext.getBean() 也是同一个对象。但是每次请求,都会重新创建对象。
- 接口:
http://127.0.0.1:8080:/dog/request
-
Session(会话范围)
- 每个 HTTP 会话创建一个新的 Bean 实例。
- 适用于用户会话相关数据。
- 在一个 session 当中,多次请求,获取的对象都是同一个。但是我们换一个浏览器访问会重新创建对象(另外一个 session)。
- 接口:
http://127.0.0.1:8080:/dog/session
-
Application(应用范围)
- 每个 ServletContext 创建一个 Bean 实例。
- Application scope 就是对于整个 web 容器来说,bean 的作用域是 ServletContext 级别的。这个和 singleton 有点类似,区别在于:Application scope 是 ServletContext 的单例,singleton 是一个 ApplicationContext 的单例。在一个 web 容器中 ApplicationContext 可以有多个。
- 接口:
http://127.0.0.1:8080:/dog/application
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 setDogComponent(DogComponent dogComponent) {
this.dogComponent = dogComponent;
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();
}
流程顺序为:实例化 -> 属性赋值 -> BeanNameAware -> 初始化方法 -> 使用 Bean -> 销毁 Bean。
3. Spring Boot 自动配置流程
Spring Boot 的自动配置是指当 Spring 容器启动后,一些配置类、Bean 对象等就自动存入了 IoC 容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
Spring Boot 通过自动配置(Auto-Configuration)简化了配置过程,以下是其核心流程:
数据准备
import org.springframework.context.annotation.Configuration;
@Configuration
public class SilverConfig {
public void study() {
System.out.println("SilverConfig study... ");
}
}
获取 SilverConfig 这个 Bean,写测试用例:
@Autowired
ApplicationContext context;
@Test
void testBean() {
SilverConfig bean = context.getBean(SilverConfig.class);
System.out.println(bean);
}
如果测试报错,通常是因为该类没有和 SpringBoot 启动类在同一个包下面。Sliqverconfig 类相当于第三方包,我们需要指定路径或者引入的文件,告诉 Spring 进行扫描。
解决方案
@ComponentScan(basePackages = "com.config")
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
@Import(SilverConfig.class)
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
ImportSelector 接口实现类
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.config.SilverConfig"};
}
}
@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 EnableSilverConfig {}
注解中封装 @Import 注解,导入 MySelector.class。
@EnableSilverConfig
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
Spring Boot 配置流程如下:通过 @EnableXXX 注解触发 ImportSelector 或 ImportBeanDefinitionRegistrar,进而加载对应的配置类和 Bean。
4. 总结
Spring 的 Bean 管理和生命周期机制是其核心功能,而 Spring Boot 的自动配置流程则大大简化了配置工作,帮助开发者快速构建应用。