深入探索Spring:Bean管理与Spring Boot自动配置原理

Spring 原理深入探索

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[实体类]

typescript 复制代码
public class Dog {
    private  String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

AI写代码java
运行
123456789

创建一个DogCompoent类(交给Spring进行管理):

typescript 复制代码
@Component
public class DogCompoent {
    @Bean
    public Dog dog(){
        return new Dog();
    }
//    单例
    @Bean
    public Dog singleDog(){
        return new Dog();
    }
//    多例
    @Bean
    @Scope("prototype")
    public Dog prototypeDog(){
        return new Dog();
    }
//request
    @Bean
    @RequestScope
    public Dog requestDog(){
        return new Dog();
    }
 //session
    @Bean
    @SessionScope
    public Dog sessionDog(){
        return new Dog();
    }
    //application
    @Bean
    @ApplicationScope
    public Dog applicationDog(){
        return new Dog();
    }
}

AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536

定义一个controller类:

kotlin 复制代码
@RestController
@RequestMapping("/dog")
public class DogController {
    @Autowired
    ApplicationContext context;
      //  单例作用域
    @Resource(name = "singleDog")
    Dog singleDog;
    //    多例作用域(原型)
    @Resource(name="prototypeDog")
    Dog prototypeDog;
//
    @Resource(name = "requestDog")
    Dog requestDog;
//
    @Resource(name = "sessionDog")
    Dog sessionDog;
//
    @Resource(name = "applicationDog")
    Dog applicationDog;
    @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;
    }
}

AI写代码java
运行
12345678910111213141516171819202122232425262728293031323334353637383940414243444546

启动类:

typescript 复制代码
@SpringBootApplication
public class SpringPrincipleApplication {
	public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
	}
}

AI写代码java
运行
123456

启动项目: 测试不同作用域的Bean取到的对象是否一样:

  • Singleton(单例)

    • Spring默认的作用域,所有客户端共享同一个Bean实例。
    • 适用于无状态Bean。

单例作用域的Bean:http://127.0.0.1:8080:/dog/singleton

多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和applicationContext.getBean()也是同⼀个对象。


  • Prototype(原型)

    • 每次注入或获取Bean时,都会创建一个新的实例。
    • 适用于有状态Bean。

原型作用域的Bean:http://127.0.0.1:8080:/dog/prototype

观察ContextDog, 每次获取的对象都不⼀样(注入的对象在Spring容器启动时, 就已经注入了, 所以多次请求也不会发生变化)


  • Request(请求范围)

    • 每个HTTP请求创建一个新的Bean实例。
    • 适用于Web应用中的请求相关数据。
      request作用域的Bean:http://127.0.0.1:8080:/dog/request

在⼀次请求中, @Autowired 和 applicationContext.getBean() 也是同⼀个对象.但是每次请求, 都会重新创建对象.


  • Session(会话范围)

    • 每个HTTP会话创建一个新的Bean实例。
    • 适用于用户会话相关数据。

session作用域的Bean:http://127.0.0.1:8080:/dog/session

在一个session当中,多次请求,获取的对象都是同一个。

但是我们换一个浏览器访问会重新创建对象(另外一个session)

  • Application(应用范围)

    • 每个ServletContext创建一个Bean实例。

session作用域的Bean:http://127.0.0.1:8080:/dog/application

在⼀个应用中, 多次访问都是同一个对象.

Application scope就是对于整个web容器来说, bean的作⽤域是ServletContext级别的. 这个和

singleton有点类似,区别在于: Application scope是ServletContext的单例, singleton是⼀个

ApplicationContext的单例. 在⼀个web容器中ApplicationContext可以有多个。

1.2 Bean 的生命周期

Bean的生命周期\]从创建到销毁,Spring对其进行了详细的管理: 1. **Bean的创建** * 通过构造器或工厂方法创建Bean实例。 2. **依赖注入** * 根据配置注入Bean的依赖项。 3. **初始化回调** * 调用Bean的初始化方法(如`@PostConstruct`注解标注的方法)。 * Spring的`InitializingBean`接口或自定义初始化方法。 4. **Bean可用** * Bean已经准备就绪,可以被应用程序使用。 5. **销毁回调** * 当应用上下文关闭时,调用Bean的销毁方法。 * 如`@PreDestroy`注解标注的方法,或`DisposableBean`接口。 *** ** * ** *** > 创建一个BeanLifeComponent类继承BeanNameAware来说明Bean的生命周期从创建到销毁。 **代码:** ```typescript 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 { // // @Autowired private DogCompoent dogCompoent; //1. 实例化 public BeanLifeComponent() { System.out.println("执行构造方法...."); } // 2.属性赋值(seter方法注入) @Autowired public void setDogCompoent(DogCompoent dogCompoent) { this.dogCompoent = dogCompoent; System.out.println("执行属性赋值"); } //获取Bean的名称 @Override public void setBeanName(String name) { System.out.println("执行BeanNameAware,beanName:"+name); } // 初始化方法 @PostConstruct public void init(){ System.out.println("初始化方法..."); } // 4.使用Bean public void use(){ System.out.println("使用Bean,执行use 方法"); } // 5.销毁Bean @PreDestroy public void destroy(){ System.out.println("销毁bean"); } } AI写代码java 运行 123456789101112131415161718192021222324252627282930313233343536373839404142 ``` 启动项目: ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/0dc2011da7c0dfce4708ba82ab19c8e8.webp) 进行测试: ```csharp @Test void testBean(){ BeanLifeComponent bean = context.getBean(BeanLifeComponent.class); bean.use(); } AI写代码java 运行 12345 ``` ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/b6876ea9114c8befbb2c1d3b9cf0c0c3.webp) 可以看到使用Bean成功了。 **流程:** ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/d29479d56f0216530350b5e91ccc2245.webp) ### [](https://link.juejin.cn?target= "")[](https://link.juejin.cn?target= "")2. Spring Boot 自动配置流程 > SpringBoot的自动配置就是当Spring容器启动后, ⼀些配置类, bean对象等就自动存入到了IoC容器中,不需要我们手动去声明, 从而简化了开发, 省去了繁琐的配置操作。 > > SpringBoot自动配置, 就是指SpringBoot是如何将依赖jar包中的配置类以及Bean加载到Spring IoC容器中的Spring Boot通过自动配置(Auto-Configuration)简化了配置过程,以下是其核心流程: > 数据准备 ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/6eb0b52817d093e7950bfad5deb906e9.webp) ```kotlin import org.springframework.context.annotation.Configuration; //第三方 @Configuration public class Sliqverconfig { public void study(){ System.out.println("Sliqverconfig study... "); } } AI写代码java 运行 12345678 ``` *** ** * ** *** > 获取Sliqverconfig这个Bean > > 写测试用例: ```scss @Autowired ApplicationContext context; @Test void testBean(){ Sliqverconfig bean = context.getBean(Sliqverconfig.class); System.out.println(bean); } AI写代码java 运行 1234567 ``` ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/9d7b462f4ac521d6195523dfa346bbdd.webp) 可以看到测试报错,那这个是什么原因呢? > 原因分析 Spring通过五⼤注解和 @Bean 注解可以帮助我们把Bean加载到SpringIoC容器中, 以上有个前提就是这些注解类需要和SpringBoot启动类在同⼀个目录下 ( @SpringBootApplication 标注的类 就是SpringBoot项目的启动类。 ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/0c63e6c2ff7b2b7bc74cdd701b69355d.webp) 可以看到这个`Sliqverconfig`类并不和启动类在同一个包下面。 这个`Sliqverconfig`类相当于第三方包,那我们怎么样把这个包,交给Spring管理这些Bean呢? > 解决方案 我们需要指定路径或者引入的文件, 告诉Spring, 让Spring进行扫描到. 常见的解决方法有两种: `1. @ComponentScan 组件扫描` ```less @ComponentScan(basePackages = "com.config") @SpringBootApplication public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } } AI写代码java 运行 1234567 ``` 再进行测试: ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/a296cfda0b643b8bbea56696c250a8ae.webp) 获取成功. `2. @Import` > 导入类 ```less @Import(Sliqverconfig.class) @SpringBootApplication public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } } AI写代码java 运行 1234567 ``` 再进行测试: ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/a296cfda0b643b8bbea56696c250a8ae.webp) 获取成功. *** ** * ** *** > 导⼊ ImportSelector 接口实现类 ```typescript public class MySelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.config.Sliqverconfig"}; } } AI写代码java 运行 1234567 ``` 启动类: ```less @Import(MyRegistrar.class) @SpringBootApplication public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } } AI写代码java 运行 1234567 ``` 再进行测试: ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/a296cfda0b643b8bbea56696c250a8ae.webp) 获取成功. **问题** : 但是他们都有⼀个明显的问题, 就是使用者需要知道第三方依赖中有哪些Bean对象或配置类. 如果漏掉其中⼀些Bean, 很可能导致我们的项目出现大的事故.这对程序员来说非常不友好. 依赖中有哪些Bean, 使用时候需要配置哪些bean, 第三方依赖最清楚, **那能否由第三方依赖来做这件事呢** ? 比较常见的方法就是第三方依赖给我们提供⼀个注解, 这个注解⼀般都以@EnableXxxx开头的注解,注解中封装的就是 @Import 注解. > 第三⽅依赖提供注解. ```java 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 { } AI写代码java 运行 123456789101112131415 ``` 注解中封装 @Import 注解, 导入MySelector.class 启动类: 直接使用第三方提供的注解: ```less //通过第三方注解 @EnableSliqversConfig @EnableSliqversConfig @SpringBootApplication public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } } AI写代码java 运行 12345678 ``` 再进行测试: ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/a296cfda0b643b8bbea56696c250a8ae.webp) 获取成功. *** ** * ** *** Spring boot 配置流程如下: ![在这里插入图片描述](https://oss.xyyzone.com/jishuzhan/article/1971129453964738561/fdf4f5d5bef2c841bd6951e99baab81b.webp) ### [](https://link.juejin.cn?target= "")[](https://link.juejin.cn?target= "")3.总结 Spring的Bean管理和生命周期机制是其核心功能,而Spring Boot的自动配置流程则大大简化了配置工作,帮助开发者快速构建应用

相关推荐
我是天龙_绍2 小时前
iframe 的 src 链接里带了参数(比如 token 或签名),想在 Nginx 层做鉴权
后端
大佐不会说日语~2 小时前
若依框架 (Spring Boot 3) 集成 knife4j 实现 OpenAPI 文档增强
spring boot·后端·python
Java微观世界2 小时前
枚举不止是常量!Java枚举的高级用法与单例最佳实践
后端
hhh小张2 小时前
HttpServlet(4):Cookie🍪与Session💻
后端
lecepin2 小时前
AI Coding 资讯 2025-09-25
前端·javascript·后端
舒一笑3 小时前
PandaCoder 1.1.8 发布:中文开发者的智能编码助手全面升级
java·后端·intellij idea
这周也會开心3 小时前
Spring-MVC
java·spring·mvc
少妇的美梦3 小时前
Spring Boot搭建MCP-SERVER,实现Cherry StudioMCP调用
后端·mcp
SimonKing3 小时前
跨域,总在发OPTIONS请求?这次终于搞懂CORS预检了
java·后端·程序员