Spring(三):JavaBean的生命週期

JavaBean的生命週期

一:基本概念

bean 就是由IOC 容器初始化、裝配及管理的對象。
Spring中的bean默認都是單例的,那麼單例Bean在多線程程序下如何保證線程安全呢?
Spring的單例是基於BeanFactory也就是Spring容器的,單例Bean在此容器內只有一個,Java的單例是基於 JVM,每個 JVM 內只有一個實例。所以非分佈式服務情況下,單例Bean可以保證線程安全。

二:Bean的作用範圍

創建一個bean定義,其實質是用該bean定義對應的類來創建真正實例的“配方”。把bean定義看成一個配方很有意義,它與class很類似,只根據一張“處方”就可以創建多個實例。不僅可以控制注入到對象中的各種依賴和配置值,還可以控制該對象的作用域。這樣可以靈活選擇所建對象的作用域,而不必在Java Class級定義作用域。
Spring Framework支持五種作用域,分別闡述如下表。
我眼中的Bean其實就是一個機器模板,根據一張模板,可以生產出不同的類型機器。
javaBean作用域
五種作用域中,request、session 和 global session 三種作用域僅在基於web的應用中使用(不必關心你所採用的是什麼web應用框架),只能用在基於 web 的 Spring ApplicationContext 環境。

詳細描述如下:

  1. singleton——唯一 bean 實例

當一個 bean 的作用域爲 singleton,那麼Spring IoC容器中只會存在一個共享的 bean 實例,並且所有對 bean 的請求,只要 id 與該 bean 定義相匹配,則只會返回bean的同一實例。 singleton 是單例類型(對應於單例模式),就是在創建起容器時就同時自動創建了一個bean的對象,不管你是否使用,但我們可以指定Bean節點的 lazy-init=”true” 來延遲初始化bean,這時候,只有在第一次獲取bean時纔會初始化bean,即第一次請求該bean時才初始化。 每次獲取到的對象都是同一個對象。注意,singleton 作用域是Spring中的缺省作用域。要在XML中將 bean 定義成 singleton ,可以這樣配置:

<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

也可以通過 @Scope 註解(它可以顯示指定bean的作用範圍。)的方式

@Service
@Scope("singleton")
public class ServiceImpl{

}
  1. prototype——每次請求都會創建一個新的 bean 實例

當一個bean的作用域爲 prototype,表示一個 bean 定義對應多個對象實例。 prototype 作用域的 bean 會導致在每次對該 bean 請求(將其注入到另一個 bean 中,或者以程序的方式調用容器的 getBean() 方法**)時都會創建一個新的 bean 實例。prototype 是原型類型,它在我們創建容器的時候並沒有實例化,而是當我們獲取bean的時候纔會去創建一個對象,而且我們每次獲取到的對象都不是同一個對象。根據經驗,對有狀態的 bean 應該使用 prototype 作用域,而對無狀態的 bean 則應該使用 singleton 作用域。 在 XML 中將 bean 定義成 prototype ,可以這樣配置:

<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
 或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/> 

通過 @Scope 註解的方式實現就不做演示了。

注意事項:
作用域爲 prototype 的 bean ,其destroy方法並沒有被調用。如果 bean 的 scope 設爲prototype時,當容器關閉時,destroy 方法不會被調用。對於 prototype 作用域的 bean,有一點非常重要,那就是 Spring不能對一個 prototype bean 的整個生命週期負責:容器在初始化、配置、裝飾或者是裝配完一個prototype實例後,將它交給客戶端,隨後就對該prototype實例不聞不問了。 不管何種作用域,容器都會調用所有對象的初始化生命週期回調方法。但對prototype而言,任何配置好的析構生命週期回調方法都將不會被調用。清除prototype作用域的對象並釋放任何prototype bean所持有的昂貴資源,都是客戶端代碼的職責讓Spring容器釋放被prototype作用域bean佔用資源的一種可行方式是,通過使用bean的後置處理器,該處理器持有要被清除的bean的引用)。
談及prototype作用域的bean時,在某些方面你可以將Spring容器的角色看作是Java new操作的替代者,任何遲於該時間點的生命週期事宜都得交由客戶端來處理。
Spring 容器可以管理 singleton 作用域下 bean 的生命週期,在此作用域下,Spring 能夠精確地知道bean何時被創建,何時初始化完成,以及何時被銷燬。而對於 prototype 作用域的bean,Spring只負責創建,當容器創建了 bean 的實例後,bean 的實例就交給了客戶端的代碼管理,Spring容器將不再跟蹤其生命週期,並且不會管理那些被配置成prototype作用域的bean的生命週期。

  1. request——每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP request內有效

request只適用於Web程序,每一次 HTTP 請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效,當請求結束後,該對象的生命週期即告結束。 在 XML 中將 bean 定義成 request ,可以這樣配置:

<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
  1. session——每一次HTTP請求都會產生一個新的 bean,該bean僅在當前 HTTP session 內有效

session只適用於Web程序,session 作用域表示該針對每一次 HTTP 請求都會產生一個新的 bean,同時該 bean 僅在當前 HTTP session 內有效.與request作用域一樣,可以根據需要放心的更改所創建實例的內部狀態,而別的 HTTP session 中根據 userPreferences 創建的實例,將不會看到這些特定於某個 HTTP session 的狀態變化。當HTTP session最終被廢棄的時候,在該HTTP session作用域內的bean也會被廢棄掉。

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
  1. globalSession

global session 作用域類似於標準的 HTTP session 作用域,不過僅僅在基於 portlet 的 web 應用中才有意義。Portlet 規範定義了全局 Session 的概念,它被所有構成某個 portlet web 應用的各種不同的 portle t所共享。在global session 作用域中定義的 bean 被限定於全局portlet Session的生命週期範圍內。

<bean id="user" class="com.foo.Preferences "scope="globalSession"/>

三:bean的生命週期

在這裏插入圖片描述
引用:https://zhuanlan.zhihu.com/p/108198655

Bean 的生命週期概括起來就是 4 個階段

  • 實例化(Instantiation)
  • 屬性賦值(Populate)
  • 初始化(Initialization)
  • 銷燬(Destruction)

實例化過程:
BeanWrapper調用createBeanInstance方法。

屬性賦值:
bean 設置相關屬性和依賴

初始化:

(主要了解Bean在實例)

Aware接口:
  • Spring 檢測到 bean 實現了 Aware 接口,則會爲其注入相應的依賴。
BeanFactory 類型的容器
  • BeanNameAware:注入當前 bean 對應 beanName;
  • BeanClassLoaderAware:注入加載當前 bean 的 ClassLoader(類加載器);
  • BeanFactoryAware:注入 當前BeanFactory容器 的引用。
ApplicationContext 類型的容器
  • EnvironmentAware:注入 Enviroment,一般用於獲取配置屬性;
  • EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),一般用於參數解析;
  • ApplicationContextAware(ResourceLoader、ApplicationEventPublisherAware、MessageSourceAware):注入 ApplicationContext 容器本身。

BeanPostProcessor後置處理器

public interface BeanPostProcessor {
	// 初始化前置處理
	default Object postProcessBeforeInitialization(Object bean,
                  String beanName) throws BeansException {
		return bean;
	}
	// 初始化後置處理
	default Object postProcessAfterInitialization(Object bean,
                  String beanName) throws BeansException {
		return bean;
	}
}

InitializingBean 和 init-method 是 Spring 爲 bean 初始化提供的擴展點。(可以擴展初始化邏輯)

DisposableBean 和 destory-method是 Spring 爲 bean 銷燬提供的擴展點。

bean生命週期詳細過程如下:(結合學習)
  1. ResouceLoader加載配置信息
  2. 解析配置信息,生成一個一個的BeanDefintion
  3. BeanDefintion由BeanDefintionRegistry管理起來
  4. BeanFactoryPostProcessor對配置信息進行加工(也就是處理配置的信息,一般通過PropertyPlaceholderConfigurer來實現)
  5. 實例化Bean
  6. 如果該Bean配置/實現了InstantiationAwareBean,則調用對應的方法
  7. 使用BeanWarpper來完成對象之間的屬性配置(依賴)
  8. 如果該Bean配置/實現了Aware接口,則調用對應的方法
  9. 如果該Bean配置了BeanPostProcessor的before方法,則調用
  10. 如果該Bean配置了init-method或者實現InstantiationBean,則調用對應的方法
  11. 如果該Bean配置了BeanPostProcessor的after方法,則調用
  12. 將對象放入到HashMap中
  13. 最後如果配置了destroy或者DisposableBean的方法,則執行銷燬操作

總結:

首先是實例化、屬性賦值、初始化、銷燬這 4 個大階段
再是初始化的具體操作,有 Aware 接口的依賴注入BeanPostProcessor 在初始化前後的處理以及 InitializingBean 和 init-method 的初始化操作;(或**@PostConstruct註解**)
銷燬的具體操作,有註冊相關銷燬回調接口,最後通過DisposableBean 和 destory-method 進行銷燬。(@PreDestory註解也可實現)

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章