SpringBean管理與Spring Boot自動(dòng)配置原理解析
Spring 原理深入探索
1. Bean 的作用域和生命周期
1.1 Bean 的作用域
在Spring中,Bean的作用域(Scope)決定了Bean的實(shí)例化方式以及其生命周期。以下是Spring中常見(jiàn)的Bean作用域:
| 作用域 | 說(shuō)明 |
|---|---|
| singleton | 每個(gè)Spring IoC容器內(nèi)同名稱的bean只有?個(gè)實(shí)例(單例)(默認(rèn) ) |
| prototype | 每次使用該bean時(shí)會(huì)創(chuàng)建新的實(shí)例(非單例) |
| request | 每個(gè)HTTP 請(qǐng)求生命周期內(nèi), 創(chuàng)建新的實(shí)例 |
| session | 每個(gè)HTTP Session生命周期內(nèi), 創(chuàng)建新的實(shí)例 |
| application | 每個(gè)ServletContext生命周期內(nèi), 創(chuàng)建新的實(shí)例 |
| websocket | 每個(gè)WebSocket生命周期內(nèi), 創(chuàng)建新的實(shí)例 |
我們直接上代碼 后面根據(jù)運(yùn)行結(jié)果觀察Bean的作用域。
創(chuàng)建一個(gè)Dog實(shí)體類:
public class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}創(chuàng)建一個(gè)DogCompoent類(交給Spring進(jìn)行管理):
@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();
}
}定義一個(gè)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 = "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;
}
}啟動(dòng)類:
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}啟動(dòng)項(xiàng)目: 測(cè)試不同作用域的Bean取到的對(duì)象是否一樣:
Singleton(單例)- Spring默認(rèn)的作用域,所有客戶端共享同一個(gè)Bean實(shí)例。
- 適用于無(wú)狀態(tài)Bean。
單例作用域的Bean:http://127.0.0.1:8080:/dog/singleton

多次訪問(wèn), 得到的都是同?個(gè)對(duì)象, 并且 @Autowired 和applicationContext.getBean()也是同?個(gè)對(duì)象。

Prototype(原型)- 每次注入或獲取Bean時(shí),都會(huì)創(chuàng)建一個(gè)新的實(shí)例。
- 適用于有狀態(tài)Bean。
原型作用域的Bean:http://127.0.0.1:8080:/dog/prototype

觀察ContextDog, 每次獲取的對(duì)象都不?樣(注入的對(duì)象在Spring容器啟動(dòng)時(shí), 就已經(jīng)注入了, 所以多次請(qǐng)求也不會(huì)發(fā)生變化)

Request(請(qǐng)求范圍)- 每個(gè)HTTP請(qǐng)求創(chuàng)建一個(gè)新的Bean實(shí)例。
- 適用于Web應(yīng)用中的請(qǐng)求相關(guān)數(shù)據(jù)。
- request作用域的Bean:
http://127.0.0.1:8080:/dog/request

在?次請(qǐng)求中, @Autowired 和 applicationContext.getBean() 也是同?個(gè)對(duì)象.但是每次請(qǐng)求, 都會(huì)重新創(chuàng)建對(duì)象.

Session(會(huì)話范圍)- 每個(gè)HTTP會(huì)話創(chuàng)建一個(gè)新的Bean實(shí)例。
- 適用于用戶會(huì)話相關(guān)數(shù)據(jù)。
session作用域的Bean:http://127.0.0.1:8080:/dog/session


在一個(gè)session當(dāng)中,多次請(qǐng)求,獲取的對(duì)象都是同一個(gè)。
但是我們換一個(gè)瀏覽器訪問(wèn)會(huì)重新創(chuàng)建對(duì)象(另外一個(gè)session)
Application(應(yīng)用范圍)- 每個(gè)ServletContext創(chuàng)建一個(gè)Bean實(shí)例。
session作用域的Bean:http://127.0.0.1:8080:/dog/application

在?個(gè)應(yīng)用中, 多次訪問(wèn)都是同一個(gè)對(duì)象.
Application scope就是對(duì)于整個(gè)web容器來(lái)說(shuō), bean的作?域是ServletContext級(jí)別的. 這個(gè)和
singleton有點(diǎn)類似,區(qū)別在于: Application scope是ServletContext的單例, singleton是?個(gè)
ApplicationContext的單例. 在?個(gè)web容器中ApplicationContext可以有多個(gè)。
1.2 Bean 的生命周期
Bean的生命周期從創(chuàng)建到銷毀,Spring對(duì)其進(jìn)行了詳細(xì)的管理:
Bean的創(chuàng)建
- 通過(guò)構(gòu)造器或工廠方法創(chuàng)建Bean實(shí)例。
依賴注入
- 根據(jù)配置注入Bean的依賴項(xiàng)。
初始化回調(diào)
- 調(diào)用Bean的初始化方法(如
@PostConstruct注解標(biāo)注的方法)。 - Spring的
InitializingBean接口或自定義初始化方法。
Bean可用
- Bean已經(jīng)準(zhǔn)備就緒,可以被應(yīng)用程序使用。
銷毀回調(diào)
- 當(dāng)應(yīng)用上下文關(guān)閉時(shí),調(diào)用Bean的銷毀方法。
- 如
@PreDestroy注解標(biāo)注的方法,或DisposableBean接口。
創(chuàng)建一個(gè)BeanLifeComponent類繼承BeanNameAware來(lái)說(shuō)明Bean的生命周期從創(chuàng)建到銷毀。
代碼:
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. 實(shí)例化
public BeanLifeComponent() {
System.out.println("執(zhí)行構(gòu)造方法....");
}
// 2.屬性賦值(seter方法注入)
@Autowired
public void setDogCompoent(DogCompoent dogCompoent) {
this.dogCompoent = dogCompoent;
System.out.println("執(zhí)行屬性賦值");
}
//獲取Bean的名稱
@Override
public void setBeanName(String name) {
System.out.println("執(zhí)行BeanNameAware,beanName:"+name);
}
// 初始化方法
@PostConstruct
public void init(){
System.out.println("初始化方法...");
}
// 4.使用Bean
public void use(){
System.out.println("使用Bean,執(zhí)行use 方法");
}
// 5.銷毀Bean
@PreDestroy
public void destroy(){
System.out.println("銷毀bean");
}
}啟動(dòng)項(xiàng)目:

進(jìn)行測(cè)試:
@Test
void testBean(){
BeanLifeComponent bean = context.getBean(BeanLifeComponent.class);
bean.use();
}
可以看到使用Bean成功了。
流程:

2. Spring Boot 自動(dòng)配置流程
SpringBoot的自動(dòng)配置就是當(dāng)Spring容器啟動(dòng)后, ?些配置類, bean對(duì)象等就自動(dòng)存入到了IoC容器中,不需要我們手動(dòng)去聲明, 從而簡(jiǎn)化了開(kāi)發(fā), 省去了繁瑣的配置操作。
SpringBoot自動(dòng)配置, 就是指SpringBoot是如何將依賴jar包中的配置類以及Bean加載到Spring IoC容器中的Spring Boot通過(guò)自動(dòng)配置(Auto-Configuration)簡(jiǎn)化了配置過(guò)程,以下是其核心流程:
數(shù)據(jù)準(zhǔn)備

import org.springframework.context.annotation.Configuration;
//第三方
@Configuration
public class Sliqverconfig {
public void study(){
System.out.println("Sliqverconfig study... ");
}
}獲取Sliqverconfig這個(gè)Bean
寫測(cè)試用例:
@Autowired
ApplicationContext context;
@Test
void testBean(){
Sliqverconfig bean = context.getBean(Sliqverconfig.class);
System.out.println(bean);
}
可以看到測(cè)試報(bào)錯(cuò),那這個(gè)是什么原因呢?
原因分析
Spring通過(guò)五?注解和 @Bean 注解可以幫助我們把Bean加載到SpringIoC容器中, 以上有個(gè)前提就是這些注解類需要和SpringBoot啟動(dòng)類在同?個(gè)目錄下 ( @SpringBootApplication 標(biāo)注的類 就是SpringBoot項(xiàng)目的啟動(dòng)類。

可以看到這個(gè)Sliqverconfig類并不和啟動(dòng)類在同一個(gè)包下面。
這個(gè)Sliqverconfig類相當(dāng)于第三方包,那我們?cè)趺礃影堰@個(gè)包,交給Spring管理這些Bean呢?
解決方案
我們需要指定路徑或者引入的文件, 告訴Spring, 讓Spring進(jìn)行掃描到.
常見(jiàn)的解決方法有兩種:
1. @ComponentScan 組件掃描
@ComponentScan(basePackages = "com.config")
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}再進(jìn)行測(cè)試:

獲取成功.
2. @Import
導(dǎo)入類
@Import(Sliqverconfig.class)
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}再進(jìn)行測(cè)試:

獲取成功.
導(dǎo)? ImportSelector 接口實(shí)現(xiàn)類
public class MySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.config.Sliqverconfig"};
}
}啟動(dòng)類:
@Import(MyRegistrar.class)
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}再進(jìn)行測(cè)試:

獲取成功.
問(wèn)題:
但是他們都有?個(gè)明顯的問(wèn)題, 就是使用者需要知道第三方依賴中有哪些Bean對(duì)象或配置類. 如果漏掉其中?些Bean, 很可能導(dǎo)致我們的項(xiàng)目出現(xiàn)大的事故.這對(duì)程序員來(lái)說(shuō)非常不友好.
依賴中有哪些Bean, 使用時(shí)候需要配置哪些bean, 第三方依賴最清楚, 那能否由第三方依賴來(lái)做這件事呢?
比較常見(jiàn)的方法就是第三方依賴給我們提供?個(gè)注解, 這個(gè)注解?般都以@EnableXxxx開(kāi)頭的注解,注解中封裝的就是 @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)//指定要導(dǎo)入的類
public @interface EnableSliqversConfig {
}注解中封裝 @Import 注解, 導(dǎo)入MySelector.class
啟動(dòng)類:
直接使用第三方提供的注解:
//通過(guò)第三方注解 @EnableSliqversConfig
@EnableSliqversConfig
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}再進(jìn)行測(cè)試:

獲取成功.
Spring boot 配置流程如下:

3.總結(jié)
Spring的Bean管理和生命周期機(jī)制是其核心功能,而Spring Boot的自動(dòng)配置流程則大大簡(jiǎn)化了配置工作,幫助開(kāi)發(fā)者快速構(gòu)建應(yīng)用。
到此這篇關(guān)于SpringBean管理與Spring Boot自動(dòng)配置原理解析的文章就介紹到這了,更多相關(guān)Spring Boot自動(dòng)配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文詳解Java屬性為什么不能是is開(kāi)頭的boolean
在Java實(shí)體類定義中,boolean類型的屬性命名常引發(fā)爭(zhēng)議,阿里巴巴Java開(kāi)發(fā)手冊(cè)建議避免使用is作為布爾類型屬性的前綴,原因在于當(dāng)實(shí)體類被序列化或反序列化時(shí),基于JavaBean規(guī)范的框架可能會(huì)移除或忽略is,導(dǎo)致不一致的字段名,文中介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
Java多線程之異步Future機(jī)制的原理和實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Java多線程之異步Future機(jī)制的原理和實(shí)現(xiàn),感興趣的小伙伴們可以參考一下2016-08-08
Spring?Boot?3.1中整合Spring?Security和Keycloak的方法
本文介紹在最新的SpringBoot3.1版本之下,如何將Keycloak和Spring?Security一起跑起來(lái),文中結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-06-06
Spring Boot 快速搭建微服務(wù)框架詳細(xì)教程
SpringBoot是為了簡(jiǎn)化Spring應(yīng)用的創(chuàng)建、運(yùn)行、調(diào)試、部署等而出現(xiàn)的,使用它可以做到專注于Spring應(yīng)用的開(kāi)發(fā),而無(wú)需過(guò)多關(guān)注XML的配置。本文重點(diǎn)給大家介紹Spring Boot 快速搭建微服務(wù)框架詳細(xì)教程,需要的的朋友參考下吧2017-09-09
shiro并發(fā)人數(shù)登錄控制的實(shí)現(xiàn)代碼
在做項(xiàng)目中遇到這樣的需求要求每個(gè)賬戶同時(shí)只能有一個(gè)人登錄或幾個(gè)人同時(shí)登錄,如果是同時(shí)登錄的多人,要么不讓后者登錄,要么踢出前者登錄,怎么實(shí)現(xiàn)這樣的功能呢?下面小編給大家?guī)?lái)了shiro并發(fā)人數(shù)登錄控制的實(shí)現(xiàn)代碼,一起看看吧2017-09-09
解決springboot引入swagger2不生效問(wèn)題
這篇文章主要為大家介紹了解決springboot引入swagger2不生效問(wèn)題的方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
一文搞懂SpringMVC中@InitBinder注解的使用
@InitBinder方法可以注冊(cè)控制器特定的java.bean.PropertyEditor或Spring Converter和 Formatter組件。本文通過(guò)示例為大家詳細(xì)講講@InitBinder注解的使用,需要的可以參考一下2022-06-06
SpringMVC注解@CrossOrigin跨域問(wèn)題詳解
這篇文章主要介紹了SpringMVC注解@CrossOrigin跨域問(wèn)題詳解,跨域是瀏覽同源策略的造成,是瀏覽器對(duì)JavaScript施加的安全限制CORS是一種可以解決跨域問(wèn)題的技術(shù),需要的朋友可以參考下2023-11-11

