目錄
一、Bean的初始化過程
1. 加載Spring Bean
2.?解析Bean的定義
3.?Bean屬性定義
4.??BeanFactoryPostProcessor 擴展接口?
5. 實例化Bean對象
6.?Aware感知
7. 初始化方法
8. 后置處理
9. destroy 銷毀?
二、Bean的單例與多例模式?
2.1?單例模式(Singleton)
2.2 多例模式(Prototype)
2.3 案例演示:
2.3.1 單例模式:
2.3.2 多例模式:
2.4?總結
三、關于bean的生命周期面試題
1.?請詳細描述Spring框架Bean的生命周期包括哪些階段?
2. 請詳細描述一下Spring Bean的初始化過程
3.?Spring Bean的銷毀過程是怎樣的?
4.?Spring Bean的后置處理器是什么?在項目中如何使用它?
5.?Spring Bean的生命周期中,哪些方法可以進行自定義操作?
6.?什么是Bean的作用域?Spring中有哪些Bean的作用域?
7.?Bean的作用域是如何實現的?請描述其原理。
8.?請解釋Spring中的延遲初始化是什么,如何配置延遲初始化?
一、Bean的初始化過程
1. 加載Spring Bean
????????通過XML、Java annotation(注解)以及Java Configuration(配置類)等方式加載Spring Bean:這是指在Spring容器啟動時,通過配置文件、注解或配置類等方式告訴Spring框架需要創建哪些Bean。
2.?解析Bean的定義
BeanDefinitionReader:這是一個解析器,它將Bean的定義解析成Spring內部的BeanDefinition結構。可以將其理解為將配置文件中的<bean>標簽轉換為Spring框架內部使用的數據結構。
可理解為:將spring.xml中的 <bean> 標簽轉換成BeanDefinition結構有點類似于XML解析。
3.?Bean屬性定義
BeanDefinition:它包含了Bean的各種屬性和方法,例如id(標識符)、class(類名)、scope(作用域)、ref(依賴的Bean)等。BeanDefinition實際上是將配置文件中的<bean>標簽的定義信息存儲到對應的BeanDefinition屬性中。
例如:
<bean id="" class="" scope=""> -----> BeanDefinition(id/class/scope)
4.??BeanFactoryPostProcessor 擴展接口?
BeanFactoryPostProcessor:這是Spring容器的擴展接口,它在Spring容器加載完BeanDefinition之后、Bean實例化之前執行。它可以對Bean的元數據(BeanDefinition)進行加工處理,例如填充或修改BeanDefinition的屬性。
5. 實例化Bean對象
BeanFactory:它是Bean的工廠,根據我們的要求生產各種不同的Bean。BeanFactory根據配置文件中的BeanDefinition信息來實例化Bean對象,并進行屬性賦值。(根據class屬性反射機制實例化對象,反射賦值設置屬性)
6.?Aware感知
Aware?感知是一種機制,它允許在Bean的生命周期中獲取對Spring容器的訪問權限。通過實現相應的Aware接口,Bean可以獲得與Spring容器的交互能力,以便在特定的生命周期階段執行自定義操作。
在Spring框架中,有多個Aware接口可供實現,每個接口都提供了不同的功能。以下是一些常見的Aware接口及其作用:
-
BeanNameAware:通過實現BeanNameAware接口,Bean可以獲取自己在Spring容器中的名稱。這對于需要知道自己在容器中的標識的Bean非常有用。
-
ApplicationContextAware:通過實現ApplicationContextAware接口,Bean可以獲取對Spring應用上下文的引用。這使得Bean能夠在需要時訪問Spring容器中的其他Bean或執行其他與容器相關的操作。
-
BeanFactoryAware:通過實現BeanFactoryAware接口,Bean可以獲取對Spring Bean工廠的引用。這使得Bean能夠在需要時訪問Bean工廠中的其他Bean或執行其他與Bean工廠相關的操作。
????????通過實現這些Aware接口,Bean可以在初始化過程中獲得對Spring容器的訪問權限,并在需要時執行特定的操作。這為開發人員提供了更大的靈活性和控制權,使得他們能夠根據自己的需求自定義Bean的行為。
7. 初始化方法
????????如果Bean定義中指定了初始化方法,容器會在屬性賦值完成后調用該方法進行一些初始化操作。可以使用@PostConstruct
注解或者實現InitializingBean
接口來指定初始化方法。如果Bean在Spring配置文件中配置了 init-method 屬性,則會自動調用其配置的初始化方法。
<bean id="paramAction" class="com.ycxw.beanLife.ParamAction" init-method="init"></bean>
8. 后置處理
BeanPostProcessor:它是一個后置處理器,在Bean對象實例化和依賴注入完成后,在顯示調用初始化方法之前或之后添加自定義的邏輯。可以將其類比為AOP中的環繞通知。
注:只有當檢測到Bean對象實現了BeanPostProcessor接口時,才會執行Before和After方法。
BeanPostProcessor的執行順序是:
Before方法 -> 初始化Bean(InitializingBean接口和init-method方法)->?After方法。
9. destroy 銷毀?
destroy:這是Bean的銷毀階段,如果這個Bean的Spring配置中配置了destroy-method屬性,在Spring容器關閉時調用。在銷毀階段,可以執行一些清理工作,例如釋放資源等。
<bean id="paramAction" class="com.ycxw.beanLife.ParamAction" destroy-method="destroy"></bean>
二、Bean的單例與多例模式?
2.1?單例模式(Singleton)
- 單例模式是指在整個應用程序中只創建一個Bean實例。
- 當使用單例模式時,Spring容器在首次請求該Bean時創建一個實例,并將其緩存起來。之后的每次請求都會返回同一個實例。
- 優點:減少了對象創建和銷毀的開銷,節省了系統資源。
- 缺點:由于共享同一個實例,可能導致狀態信息被多個線程共享,需要注意線程安全問題。
2.2 多例模式(Prototype)
- 多例模式是指每次請求時都會創建一個新的Bean實例。
- 當使用多例模式時,Spring容器在每次請求該Bean時都會創建一個新的實例,而不是返回之前創建的實例。
- 優點:每次請求都會返回一個全新的實例,避免了狀態信息共享的問題。
- 缺點:頻繁創建和銷毀對象可能導致系統資源消耗增加。
2.3 案例演示:
假設我們有一個簡單的Counter
類,用于計數:
public class Counter {private int count;public int getCount() {return count;}public void increment() {count++;}
}
我們將使用Spring配置來演示單例和多例模式的區別。
2.3.1 單例模式:
<bean id="counter" class="com.example.Counter" scope="singleton" />
public class SingletonDemo {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Counter counter1 = context.getBean("counter", Counter.class);Counter counter2 = context.getBean("counter", Counter.class);counter1.increment();System.out.println("Counter 1: " + counter1.getCount()); // 輸出:Counter 1: 1System.out.println("Counter 2: " + counter2.getCount()); // 輸出:Counter 2: 1}
}
在單例模式下,counter1
和counter2
引用的是同一個實例,因此它們的計數值是相同的。
2.3.2 多例模式:
<bean id="counter" class="com.example.Counter" scope="prototype" />
public class PrototypeDemo {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Counter counter1 = context.getBean("counter", Counter.class);Counter counter2 = context.getBean("counter", Counter.class);counter1.increment();System.out.println("Counter 1: " + counter1.getCount()); // 輸出:Counter 1: 1System.out.println("Counter 2: " + counter2.getCount()); // 輸出:Counter 2: 0}
}
在多例模式下,counter1
和counter2
引用的是不同的實例,因此它們的計數值是獨立的。
2.4?總結
????????單例模式和多例模式各有優缺點,具體使用哪種模式取決于應用程序的需求和設計考慮。在需要共享狀態或資源的場景下,可以使用單例模式;在需要獨立狀態或避免狀態共享的場景下,可以使用多例模式。
三、關于bean的生命周期面試題
1.?請詳細描述Spring框架Bean的生命周期包括哪些階段?
在Spring框架中,Bean的生命周期包括以下幾個階段:
-
實例化(Instantiation):在這個階段,Spring會根據配置或注解創建Bean的實例。這可以通過構造函數實例化、工廠方法或者Bean容器中的其他方式來實現。
-
屬性賦值(Population):在實例化后,Spring會通過依賴注入(Dependency Injection)或者屬性賦值的方式,將Bean的屬性值設置好。這可以通過setter方法注入或者字段注入來實現。
-
初始化(Initialization):在屬性賦值完成后,Spring會調用Bean的初始化方法。這個方法可以通過實現InitializingBean接口的
afterPropertiesSet()
方法,或者在配置文件中通過init-method
屬性指定。 -
使用(In Use):在初始化完成后,Bean進入可用狀態,可以被其他對象引用和使用。
-
銷毀(Destruction):當Bean不再被需要時,Spring會調用Bean的銷毀方法進行資源釋放和清理工作。這個方法可以通過實現DisposableBean接口的
destroy()
方法,或者在配置文件中通過destroy-method
屬性指定。
????????Bean的銷毀階段只有在容器關閉時才會觸發,或者在使用單例作用域的Bean時,當Bean不再被引用時被垃圾回收器回收。以上是Spring框架中Bean的典型生命周期階段,每個階段都提供了相應的擴展點,可以在不同階段進行自定義操作。
2. 請詳細描述一下Spring Bean的初始化過程
👆👆👆 請看本文章第一點內容 !!!👆👆👆
3.?Spring Bean的銷毀過程是怎樣的?
Spring Bean的銷毀過程可以分為以下幾個步驟:
-
關閉容器:當Spring容器關閉時,會觸發Bean的銷毀過程。這可以通過調用
ApplicationContext
的close()
方法或者銷毀容器的生命周期回調來實現。 -
調用Bean的銷毀方法:在容器關閉時,Spring會調用Bean的銷毀方法進行資源釋放和清理工作。這個方法可以通過實現DisposableBean接口的
destroy()
方法,或者在配置文件中通過destroy-method
屬性指定。 -
應用Bean后置處理器:在銷毀方法調用之前和之后,Spring會應用注冊的Bean后置處理器(BeanPostProcessor)。后置處理器可以對Bean進行額外的處理,例如在Bean銷毀前后進行一些自定義邏輯。
????????Bean的銷毀階段只有在容器關閉時才會觸發,或者在使用單例作用域的Bean時,當Bean不再被引用時被垃圾回收器回收。對于原型作用域的Bean,默認情況下,Spring不會管理其銷毀過程,需要開發者手動進行資源釋放。
????????同時,可以通過實現DisposableBean
接口或者在配置文件中指定destroy-method
屬性來定義Bean的銷毀方法。在銷毀方法中,可以進行一些資源釋放、數據庫連接關閉等清理工作,確保系統的正常關閉和資源的釋放。
4.?Spring Bean的后置處理器是什么?在項目中如何使用它?
Spring Bean后置處理器(BeanPostProcessor)是一種特殊類型的Bean,用于在容器實例化、配置和初始化Bean的過程中進行自定義的處理操作。
在項目中使用Bean后置處理器,可以通過以下步驟:
-
創建后置處理器類:實現
BeanPostProcessor
接口,該接口包含兩個方法:postProcessBeforeInitialization()
和postProcessAfterInitialization()
。import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 在Bean初始化之前進行自定義處理操作return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 在Bean初始化之后進行自定義處理操作return bean;} }
-
注冊后置處理器:在Spring配置文件中,將自定義的后置處理器注冊到容器中。
<bean class="com.example.MyBeanPostProcessor" />
-
啟動容器:在項目啟動時,啟動Spring容器,容器會自動識別并應用注冊的后置處理器。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
通過使用Bean后置處理器,可以在Bean的初始化前后進行自定義操作,例如對Bean進行增強、屬性修改、代理等。常見的應用場景包括:
- 屬性值注入前后的自定義處理。
- 在Bean初始化前后執行一些特定的邏輯。
- 動態代理Bean的生成。
- 對特定類型的Bean進行特殊處理等。
????????需要注意的是,Bean后置處理器會對容器中的所有Bean實例進行處理,因此在編寫后置處理器時需要注意邏輯的準確性和性能的影響。
5.?Spring Bean的生命周期中,哪些方法可以進行自定義操作?
在Spring Bean的生命周期中,可以進行自定義操作的方法主要包括以下幾個:
-
初始化方法(Initialization methods):
- 在配置文件中通過
init-method
屬性指定的方法。 - 在Bean類中實現
InitializingBean
接口,并重寫afterPropertiesSet()
方法。
初始化方法用于在Bean實例化后,屬性注入完成之后執行一些初始化邏輯,例如初始化資源、建立連接等。您可以根據需要選擇其中一種方式來定義初始化方法。
- 在配置文件中通過
-
銷毀方法(Destruction methods):
- 在配置文件中通過
destroy-method
屬性指定的方法。 - 在Bean類中實現
DisposableBean
接口,并重寫destroy()
方法。
銷毀方法用于在容器銷毀Bean之前執行一些清理操作,例如釋放資源、關閉連接等。您可以根據需要選擇其中一種方式來定義銷毀方法。
- 在配置文件中通過
-
后置處理器(Post-processors):
后置處理器可以在Bean的初始化前后對Bean進行自定義處理。
postProcessBeforeInitialization()
方法在初始化方法調用之前被調用,postProcessAfterInitialization()
方法在初始化方法調用之后被調用。通過實現這些方法,您可以對Bean進行額外的操作,例如修改屬性值、添加代理等。 - 實現
BeanPostProcessor
接口,并重寫postProcessBeforeInitialization()
和postProcessAfterInitialization()
方法。
????????除了上述方法外,還可以使用其他擴展機制來進行自定義操作,例如使用@PostConstruct
和@PreDestroy
注解來標記初始化方法和銷毀方法,或者通過實現BeanFactoryPostProcessor
和BeanDefinitionRegistryPostProcessor
接口來對Bean的定義進行修改。
????????通過自定義這些方法,您可以在Spring Bean的生命周期中添加自己的邏輯,以滿足特定的需求和業務場景。
6.?什么是Bean的作用域?Spring中有哪些Bean的作用域?
在Spring中,Bean的作用域(Scope)指的是定義了Bean的生命周期和可見范圍的一組規則。作用域決定了在不同的上下文中訪問和使用Bean的方式。
Spring框架提供了以下幾種常用的Bean作用域:
-
Singleton(默認):單例作用域。在整個應用程序中,只會創建一個Bean實例,并且所有對該Bean的請求都會返回同一個實例。這是Spring的默認作用域。
-
Prototype:原型作用域。每次請求Bean時,都會創建一個新的Bean實例。每個實例都有自己的狀態,因此在使用時需要注意管理和維護。
-
Request:請求作用域。每個HTTP請求都會創建一個新的Bean實例,并且該Bean實例僅在當前請求的范圍內可見。在同一個請求內的不同組件可以共享同一個實例。
-
Session:會話作用域。每個HTTP會話(Session)都會創建一個新的Bean實例,并且該Bean實例僅在當前會話的范圍內可見。在同一個會話內的不同請求可以共享同一個實例。
-
Global Session:全局會話作用域。僅在基于portlet的web應用中有效。它類似于Session作用域,但是不同portlet之間可以共享同一個實例。
????????除了上述常用的作用域,Spring還提供了其他一些作用域,如WebSocket作用域、Application作用域等,用于滿足特定的應用需求。
????????要指定Bean的作用域,可以使用@Scope
注解或在XML配置文件中進行配置。例如,使用@Scope("prototype")
將Bean的作用域設置為原型作用域。
????????需要根據具體的業務需求和場景選擇合適的Bean作用域。默認情況下,使用單例作用域是最常見和推薦的方式,但在某些情況下,原型作用域或其他作用域可能更適合。
7.?Bean的作用域是如何實現的?請描述其原理。
在Spring中,Bean的作用域是通過Bean的創建和管理方式來實現的。不同作用域的Bean會有不同的創建和銷毀策略。
-
Singleton作用域:
- 當容器啟動時,Spring會創建并初始化Singleton作用域的Bean實例,并將其放入容器中的單例緩存池中。
- 之后,每次請求該Bean時,容器會直接從單例緩存池中返回已經創建好的Bean實例。
- Singleton作用域的Bean在容器關閉時會被銷毀。
-
Prototype作用域:
- 當容器接收到對Prototype作用域Bean的請求時,會創建一個新的Bean實例。
- 容器會對該實例進行初始化和依賴注入,并返回給請求方。
- 對Prototype作用域的Bean,容器不會進行進一步的管理和跟蹤,不負責其生命周期的管理。因此,在使用Prototype作用域的Bean時,需要手動管理其銷毀。
-
Request作用域和Session作用域:
- 當容器接收到對Request作用域或Session作用域的Bean的請求時,會檢查當前的請求或會話中是否存在對應的Bean實例。
- 如果存在,則直接返回該實例;如果不存在,則創建一個新的Bean實例并放入請求或會話中,以便后續的訪問和共享。
- Request作用域和Session作用域的Bean在請求或會話結束時會被銷毀。
????????實現Bean作用域的關鍵在于容器的管理和跟蹤機制。容器會根據作用域的不同,采取相應的策略來創建、緩存和銷毀Bean實例。對于Singleton作用域的Bean,容器會負責管理其單例緩存池;對于Prototype作用域的Bean,容器只負責創建實例,不負責管理其生命周期;對于Request作用域和Session作用域的Bean,容器會將實例與請求或會話關聯起來,以便后續的訪問和共享。
????????需要注意的是,作用域僅僅決定了Bean實例的創建和可見范圍,并不影響Bean定義本身。因此,無論是Singleton作用域還是Prototype作用域,Bean的定義方式都是相同的,只是在創建和管理上有所區別。
8.?請解釋Spring中的延遲初始化是什么,如何配置延遲初始化?
在Spring中,延遲初始化(Lazy Initialization)是指在容器啟動時不立即創建所有的Bean實例,而是在第一次使用該Bean時才進行創建。這可以有效地減少啟動時間和資源消耗,特別是對于那些初始化耗時較長或者很少被使用的Bean。
要配置延遲初始化,可以使用兩種方式:
-
使用注解配置:
- 在類級別上使用
@Lazy
注解,表示該類的Bean實例需要延遲初始化。例如:@Lazy
。 - 在XML配置文件中,使用
lazy-init="true"
屬性來配置延遲初始化。例如:<bean id="myBean" class="com.example.MyBean" lazy-init="true" />
- 在類級別上使用
-
使用編程方式配置:
- 在Java配置類中,使用
@Lazy
注解來標記需要延遲初始化的Bean。例如:@Lazy
。 - 在XML配置文件中,使用
lazy-init="true"
屬性來配置延遲初始化。例如:<bean id="myBean" class="com.example.MyBean" lazy-init="true" />
- 在Java配置類中,使用
????????無論使用注解還是XML配置,配置了延遲初始化的Bean在容器啟動時不會被立即創建。而是在第一次被請求或者被依賴注入時才會進行實例化和初始化。這樣可以延遲Bean的創建,節省資源并提高應用程序的啟動性能。
????????需要注意的是,延遲初始化只適用于Singleton作用域的Bean。對于Prototype作用域的Bean,Spring無法延遲初始化,因為每次請求都需要創建一個新的實例。
????????延遲初始化可以根據具體的業務需求和性能考慮來選擇是否使用。對于那些啟動時間較長或者很少被使用的Bean,延遲初始化可以提高應用程序的啟動性能和資源利用率。但對于那些頻繁被使用的Bean,延遲初始化可能會導致延遲的開銷,需要權衡利弊進行選擇。