Spring?Bean作用域、生命周期與自動(dòng)裝配深入解析
SpringBoot版本:3.5.8
1.Bean的作用域
Spring Bean的作用域定義了Bean的作用范圍,即Bean在哪些上下文中可用
/**
* 實(shí)體類
*/
public class Dog {
}
/**
* 配置類
*/
@Configuration
public class DogConfig {
//單例
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public Dog singleDog(){
return new Dog();
}
//原型
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog prototypeDog(){
return new Dog();
}
//請(qǐng)求
@Bean
@RequestScope
public Dog requestDog(){
return new Dog();
}
//會(huì)話
@Bean
@SessionScope
public Dog sessionDog(){
return new Dog();
}
//應(yīng)用
@Bean
@ApplicationScope
public Dog applicationDog(){
return new Dog();
}
}
/**
* 啟動(dòng)類
*/
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
/**
* 視圖層
*/
@RequestMapping("/test")
@RestController
public class TestController {
private final Dog single;
private final Dog prototype;
private final Dog request;
private final Dog session;
private final Dog application;
private final ApplicationContext context;
public TestController(@Qualifier("singleDog") Dog single, @Qualifier("prototypeDog") Dog prototype,
@Qualifier("requestDog") Dog request, @Qualifier("sessionDog") Dog session,
@Qualifier("applicationDog") Dog application, ApplicationContext context) {
this.single = single;
this.prototype = prototype;
this.request = request;
this.session = session;
this.application = application;
this.context = context;
}
@RequestMapping("/single")
public String single(){
Dog singleDog = (Dog)context.getBean("singleDog");
return "dog: " + this.single.toString() + "
" + "contextDog: " + singleDog;
}
@RequestMapping("/prototype")
public String prototype(){
Dog prototypeDog = (Dog)context.getBean("prototypeDog");
return "dog: " + this.prototype.toString() + "
" + "contextDog: " + prototypeDog;
}
@RequestMapping("/request")
public String request(){
Dog requestDog = (Dog)context.getBean("requestDog");
return "dog: " + this.request.toString() + "
" + "contextDog: " + requestDog;
}
@RequestMapping("/session")
public String session(){
Dog sessionDog = (Dog)context.getBean("sessionDog");
return "dog: " + this.session.toString() + "
" + "contextDog: " + sessionDog;
}
@RequestMapping("/application")
public String application(){
Dog applicationDog = (Dog)context.getBean("applicationDog");
return "dog: " + this.application.toString() + "
" + "contextDog: " + applicationDog;
}
}Singleton(單例):默認(rèn)作用域,每個(gè)Spring容器中僅存在一個(gè)Bean實(shí)例

Prototype(原型):每次請(qǐng)求Bean時(shí)都會(huì)創(chuàng)建一個(gè)新的實(shí)例

- 直接通過(guò)context.getBean()獲取會(huì)觸發(fā)新實(shí)例創(chuàng)建
- 當(dāng)使用
@Autowired或@Resource注入prototype作用域的Bean時(shí):注入操作僅在初始化階段(Spring容器創(chuàng)建和配置Bean的過(guò)程)執(zhí)行一次,后續(xù)通過(guò)字段引用訪問(wèn)的是最初注入的實(shí)例,不會(huì)因后續(xù)請(qǐng)求自動(dòng)重新注入新實(shí)例 - Request(請(qǐng)求):每個(gè)HTTP請(qǐng)求創(chuàng)建一個(gè)新的Bean實(shí)例,僅在Web應(yīng)用中有效

- 1.代理注入:當(dāng)使用@Autowired注入Request作用域的Bean時(shí),Spring實(shí)際上注入的是一個(gè)代理對(duì)象而非真實(shí)實(shí)例。代理對(duì)象在應(yīng)用啟動(dòng)時(shí)就被注入到依賴它的單例Bean中,但真實(shí)實(shí)例的創(chuàng)建被延遲到HTTP請(qǐng)求發(fā)生時(shí)
- 2.方法調(diào)用:注入的代理對(duì)象內(nèi)部持有對(duì)當(dāng)前HTTP請(qǐng)求上下文的引用。當(dāng)調(diào)用代理對(duì)象的方法時(shí),代理會(huì)從當(dāng)前請(qǐng)求的上下文中查找或創(chuàng)建新的真實(shí)實(shí)例
- 3.實(shí)例操作:雖然依賴注入發(fā)生在容器初始化階段,但通過(guò)代理模式將實(shí)例的獲取延遲到實(shí)際方法調(diào)用時(shí),這種延遲查找機(jī)制確保每個(gè)請(qǐng)求線程都能獲得獨(dú)立的實(shí)例
- Session(會(huì)話):每個(gè)用戶會(huì)話創(chuàng)建一個(gè)Bean實(shí)例,僅在Web應(yīng)用中有效

Application(應(yīng)用):整個(gè)Web應(yīng)用共享一個(gè)Bean實(shí)例

2. Bean的生命周期
生命周期指的是一個(gè)對(duì)象從創(chuàng)建到銷毀的整個(gè)生命過(guò)程。Bean的生命周期分為以下5個(gè)部分:
實(shí)例化:容器通過(guò)反射調(diào)用Bean的構(gòu)造器創(chuàng)建對(duì)象實(shí)例屬性賦值:容器注入依賴的屬性值(例如@Autowired)初始化:- 通知方法調(diào)用:通過(guò)特定接口(如Spring的BeanNameAware)注入框架相關(guān)依賴或上下文信息
- 前置處理:進(jìn)行準(zhǔn)備工作,例如參數(shù)校驗(yàn)、資源加載或權(quán)限檢查。確保主邏輯具備執(zhí)行條件,避免運(yùn)行時(shí)錯(cuò)誤
- 初始化回調(diào):在對(duì)象初始化階段觸發(fā)的自定義邏輯(如實(shí)現(xiàn)InitializingBean接口的afterPropertiesSet方法)。用于完成屬性設(shè)置后的額外初始化操作
- 后置處理:在流程結(jié)束后執(zhí)行清理或結(jié)果處理(如AOP中的@After通知)
使用Bean:Bean進(jìn)入就緒狀態(tài),可被應(yīng)用程序調(diào)用銷毀Bean:容器關(guān)閉時(shí)觸發(fā)銷毀
2.1 示例
/**
* 實(shí)體類
*/
public class Cat {
}
/**
* 配置類
*/
@Configuration
public class CatConfig {
@Bean
public Cat cat(){
return new Cat();
}
}
/**
* 實(shí)現(xiàn)類
*/
@Component
@Slf4j
public class BeanLifeComponent implements BeanNameAware, BeanPostProcessor, InitializingBean {
private Cat cat;
//1.實(shí)例化:執(zhí)行構(gòu)造方法
public BeanLifeComponent() {
log.info("1.實(shí)例化:執(zhí)行構(gòu)造方法");
}
//2.屬性賦值:執(zhí)行setter方法
@Autowired
public void setCat(Cat cat) {
log.info("2.屬性賦值:執(zhí)行setter方法");
this.cat = cat;
}
//3.1 通知方法調(diào)用
@Override
public void setBeanName(String name) {
log.info("3.1 通知方法調(diào)用,bean name is {}", name);
}
//3.2 前置處理
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.info("3.3 前置處理,bean:{},beanName:{}", bean, beanName);
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
//3.3初始化回調(diào)
@Override
public void afterPropertiesSet() {
log.info("3.2初始化回調(diào)");
}
//3.4后置處理
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
log.info("3.4后置處理,bean:{},beanName:{}", bean, beanName);
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
//4.使用Bean
public void use(){
log.info("4.使用Bean");
}
//5.銷毀Bean
@PreDestroy
public void preDestroy(){
log.info("5.銷毀Bean");
}
}
/**
* 測(cè)試類
*/
@SpringBootTest
class SpringPrincipleApplicationTests {
private final ApplicationContext context;
@Autowired
public SpringPrincipleApplicationTests(ApplicationContext context) {
this.context = context;
}
@Test
public void test(){
BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
beanLifeComponent.use();
}
}
日志顯示的順序是:
- invokeInitMethods(初始化回調(diào))
- applyBeanPostProcessorsBeforeInitialization(前置處理)
- applyBeanPostProcessorsAfterInitialization(后置處理)
這看起來(lái)與我上述介紹的生命周期流程相矛盾,具體解釋放在源碼解析后
2.2 源碼解析
AbstractAutowireCapableBeanFactory 類的作用:主要負(fù)責(zé)Bean的創(chuàng)建、依賴注入以及初始化等生命周期管理
(該思維導(dǎo)圖中的圖片看不清楚,原圖放在下述流程中)

- 1.搜索AbstractAutowireCapableBeanFactory 類,并找到它的createBean方法
- 2.createBean方法中調(diào)用了doCreateBean方法

- 3.doCreateBean方法中依次調(diào)用了
createBeanInstance、populateBean和initializeBean方法
3.1

3.2

3.3 初始化:initializeBean方法中依次調(diào)用了invokeAwareMethods、applyBeanPostProcessorsBeforeInitialization、invokeInitMethods和applyBeanPostProcessorsAfterInitialization方法

3.3.1

3.3.2

3.3.3

3.3.4

2.3 日志與源碼“沖突”的原因分析
根據(jù)源碼來(lái)看,正確的生命周期流程是:前置處理 → 初始化回調(diào) → 后置處理。與上述我運(yùn)行輸出的日志相矛盾

關(guān)鍵點(diǎn):BeanPostProcessor本身也是Bean,當(dāng)Spring初始化一個(gè)BeanPostProcessor實(shí)現(xiàn)類(BeanLifeComponent類)時(shí),這個(gè)過(guò)程是遞歸的
執(zhí)行流程分析:
- 1.創(chuàng)建非BeanPostProcessor Bean(BeanLifeComponent類):
- 按照源碼順序正常執(zhí)行:BeforeInitialization → InitMethods → AfterInitialization
- 2.創(chuàng)建BeanPostProcessor Bean時(shí):
- Spring需要先讓這個(gè)BeanPostProcessor對(duì)象本身完成初始化(調(diào)用invokeInitMethods)
- 然后才能將它加入到BeanPostProcessor列表中,供后續(xù)其他Bean(BeanLifeComponent類)使用
- 但對(duì)于這個(gè)BeanPostProcessor Bean自己來(lái)說(shuō):
- 它自己的afterPropertiesSet方法會(huì)在invokeInitMethods中執(zhí)行(初始化回調(diào)階段)
- 但它自己的postProcessBeforeInitialization(前置處理)/postProcessAfterInitialization(后置)方法不會(huì)在它自己的創(chuàng)建過(guò)程中被調(diào)用!
上述日志中的情況解釋:日志顯示的是 一個(gè)BeanPostProcessor實(shí)現(xiàn)類(BeanLifeComponent類)的初始化過(guò)程:
- 3.2 初始化回調(diào) - 這個(gè)BeanPostProcessor Bean自己的afterPropertiesSet()
- 3.3 前置處理 - BeanLifeComponent類對(duì)這個(gè)BeanPostProcessor Bean的處理
- 3.4 后置處理 - BeanLifeComponent類對(duì)這個(gè)BeanPostProcessor Bean的處理
3.SpringBoot自動(dòng)裝配
作用:自動(dòng)注冊(cè)Bean到Spring容器,不需要手動(dòng)配置,通過(guò)約定大于配置的方式減少手動(dòng)配置的復(fù)雜性。換言之,SpringBoo的自動(dòng)配置就是將依賴Jar包中的配置類以及Bean加載到Ioc容器的過(guò)程
3.1 SpringBoot加載Bean
在pom.xml文件中引入第三方依賴,實(shí)際上就是將第三方代碼引入到SpringBoot項(xiàng)目中。SpringBoot項(xiàng)目在啟動(dòng)時(shí)能識(shí)別這些依賴并自動(dòng)將它們的配置類以及Bean加載到Ioc容器的過(guò)程。下面,我將編寫(xiě)代碼作為第三方依賴,深入解析SpringBoot加載Bean的原理
創(chuàng)建目錄

編寫(xiě)代碼
/**
* 配置類
*/
@Slf4j
@Component
public class TestConfig {
public void demo(){
log.info("demo");
}
}
/**
* 測(cè)試類
*/
@SpringBootTest
public class SpringPrincipleApplicationTests {
private final ApplicationContext context;
@Autowired
public SpringPrincipleApplicationTests(ApplicationContext context) {
this.context = context;
}
@Test
public void demo(){
TestConfig testConfig = context.getBean(TestConfig.class);
testConfig.demo();
}
}運(yùn)行項(xiàng)目

- 錯(cuò)誤日志解析:在Java外功精要(2)——Spring IoC&DI一文中有詳細(xì)介紹。Spring通過(guò)
五大注解 + @Bean可以將Bean加載到Ioc容器中,前提是這些注解類需要保證和SpringBoot啟動(dòng)類(@SpringBootApplication)在同一目錄或者其子目錄下
下面介紹幾種解決方法
3.1.1 @ComponentScan
作用:告訴Spring容器去哪里掃描那些被@Component、@Service、@Repository、@Controller等注解標(biāo)記的類,并將它們自動(dòng)注冊(cè)為Bean
@SpringBootApplication
@ComponentScan("com.example.springprincicle.component")
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}

3.1.2 @Import
作用:用于將一個(gè)或多個(gè)配置類、組件類或其他類導(dǎo)入到當(dāng)前的Spring應(yīng)用上下文中
用法一:導(dǎo)入單個(gè)類
@SpringBootApplication
@Import(TestConfig.class)
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}

用法二:ImportSelector接口實(shí)現(xiàn)類
@Slf4j
@Component
public class DemoConfig {
public void demo(){
log.info("demo");
}
}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.example.springprincicle.component.TestConfig",
"com.example.springprincicle.component.DemoConfig"};
}
}
@SpringBootTest
public class SpringPrincipleApplicationTests {
private final ApplicationContext context;
@Autowired
public SpringPrincipleApplicationTests(ApplicationContext context) {
this.context = context;
}
@Test
public void demo(){
TestConfig testConfig = context.getBean(TestConfig.class);
testConfig.demo();
DemoConfig demoConfig = context.getBean(DemoConfig.class);
demoConfig.demo();
}
}
3.1.3 自定義注解
在使用@Import注解導(dǎo)入Bean時(shí),需要程序員熟悉第三方依賴的所有配置類、組件類或其他類,這對(duì)于程序員開(kāi)發(fā)程序十分不友好。所以,應(yīng)該由依賴的開(kāi)發(fā)者來(lái)做這件事
比較常見(jiàn)的方案就是第三方依賴給我們提供一個(gè)注解,該注解內(nèi)部封裝@Import注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public @interface EnableTestConfig {
}
@SpringBootApplication
@EnableTestConfig
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}3.2 @SpringBootApplication源碼解析
@SpringBootApplication是一個(gè)組合注解,包含以下三個(gè)核心注解的功能

3.2.1 @ComponentScan

- 排除不需要掃描的配置類,防止自動(dòng)配置類被重復(fù)掃描或注冊(cè)
- 在
@SpringBootApplication注解中,@ComponentScan注解沒(méi)有指定掃描路徑,那么默認(rèn)掃描路徑為 @SpringBootApplication標(biāo)注的類的類路徑
3.2.2 @SpringBootConfiguration

- 標(biāo)記該類為Spring的配置類
- @Indexed:為Spring的組件掃描提供索引支持,加速應(yīng)用啟動(dòng)時(shí)的類加載過(guò)程
3.2.3 @EnableAutoConfiguration

AutoConfigurationImportSelector類:它實(shí)現(xiàn)了 DeferredImportSelector 接口,負(fù)責(zé)在Spring 應(yīng)用啟動(dòng)時(shí)動(dòng)態(tài)加載自動(dòng)配置類
//JDK源碼,AutoConfigurationImportSelector類
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes):在Spring Boot 3.5.8(本文所用的版本)中,該方法不僅加載 Spring Boot 默認(rèn)的自動(dòng)配置類,還會(huì)加載所有第三方庫(kù)提供的自動(dòng)配置類
//JDK源碼,AutoConfigurationImportSelector類
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation,
getBeanClassLoader());//掃描類路徑下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
List<String> configurations = importCandidates.getCandidates();//從 ImportCandidates 對(duì)象中獲取所有找到的自動(dòng)配置類名,這些類名是從 .imports 文件中讀取的
Assert.state(!CollectionUtils.isEmpty(configurations),
"No auto configuration classes found in " + "META-INF/spring/"
+ this.autoConfigurationAnnotation.getName() + ".imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
- 遵循 Spring Boot 的"約定優(yōu)于配置"理念,第三方依賴庫(kù)需要將其自動(dòng)配置類定義在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中,這樣 Spring Boot 啟動(dòng)時(shí)就能自動(dòng)發(fā)現(xiàn)并加載這些配置

fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions):體現(xiàn)了Spring Framework/Spring Boot的向后兼容性設(shè)計(jì)。即使在 Spring Boot 3.x 中引入了新的 .imports 機(jī)制,Spring Framework 仍然保留了 spring.factories 的支持
//JDK源碼,AutoConfigurationImportSelector類
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
return forDefaultResourceLocation(classLoader).load(factoryType);
}
public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";4.Gitee
Gitee地址:九轉(zhuǎn)蒼翎
本文源碼:spring-principle
到此這篇關(guān)于拆解Spring Bean作用域、生命周期與自動(dòng)裝配的文章就介紹到這了,更多相關(guān)Spring Bean作用域、生命周期與自動(dòng)裝配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析Spring RestTemplate必須搭配MultiValueMap的理由
本文給大家介紹Spring RestTemplate必須搭配MultiValueMap的理由,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-11-11
SpringBoot項(xiàng)目集成Swagger和swagger-bootstrap-ui及常用注解解讀
這篇文章主要介紹了SpringBoot項(xiàng)目集成Swagger和swagger-bootstrap-ui及常用注解解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot集成mqtt的多模塊項(xiàng)目配置詳解
這篇文章主要介紹了SpringBoot集成mqtt的多模塊項(xiàng)目配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Spring的BeanUtils.copyProperties屬性復(fù)制避坑指南
這篇文章主要介紹了Spring的BeanUtils.copyProperties屬性復(fù)制避坑指南,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
關(guān)于springboot的跨域配置問(wèn)題的解決方案
這篇文章主要介紹了關(guān)于springboot的跨域配置問(wèn)題,處理filter,spring?security等過(guò)濾器跨域問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
MyBatis-Plus如何實(shí)現(xiàn)自動(dòng)加密解密
這篇文章主要介紹了MyBatis-Plus實(shí)現(xiàn)自動(dòng)加密解密方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09

