SpringBean管理与Spring Boot自动配置原理解析
目录
- Spring 原理深入探索
- 1. Bean 的作用域和生命周期
- 1.1 Bean 的作用域
- 1.2 Bean 的生命周期
- 2. Spring Boot 自动配置流程
- 3.总结
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实体类:
public class Dog { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
创建一个DogCompoent类(交给Spring进行管理):
@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(); } }
定义一个controller类:
@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 = "sessionDandroidog") 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; } }
启动类:
@SpringBootApplication public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } }
启动项目: 测试不同作用域的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对其进行了详细的管理:
Bean的创建
- 通过构造器或工厂方法创建Bean实例。
依赖注入
- 根据配置注入Bean的依赖项。
初始化回调
- 调用Bean的初始化方法(如
@PostConstruct
注解标注的方法)。 - Spring的
InitializingBean
接口或自定义初始化方法。
Bean可用
- 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 BeanjavascriptNameAware { // // @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"); } }
启动项目:
进行测试:
@Test void testBean(){ BeanLifeComponent bean = context.getBean(BeanLifeComponent.class); bean.use(); }
可以看到使用Bean成功了。
流程:2. Spring Boot 自动配置流程
SpringBoot的自动配置就是当Spring容器启动后, ⼀些配置类, bean对象等就自动存入到了IoC容器中,不需要我们手动去声明, 从而简化了开发, 省去了繁琐的配置操作。
SpringBoot自动配置, 就是指SpringBoot是如何将依赖jar包中的配置类以及Bean加载到Spring IoC容器中的Spring Boot通过自动配置(Auto-Configuration)简化了配置过程,以下是其核心流程:
数据准备
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
类相当于第三方包,那我们怎么样把这个包,交给androidSpring管理这些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); } }
再进行测试:
获取成功.
导⼊ ImportSelector 接口实现www.devze.com类
public class MySelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.config.Sliqverconfig"}; } }
启动类:
@Import(MyRegistrar.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 @EnableSliqversConfig @SpringBootApplication public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } }
再进行测试:
获取成功.
Spring boot 配置流程如下:
3.总结
Spring的Bean管理和生命周期机制是其核心功能,而Spring Boot的自动配置流程则大大简化了配置工作,帮助开发者快速构建应用。
到此这篇关于SpringBean管理与Spring Boot自动配置原理解析的文章就介绍到这了,更多相关Spring Boot自动配置内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论