Spring IOC中的Bean對(duì)象用法
Spring IOC中的Bean對(duì)象
一、Bean是什么
突然發(fā)現(xiàn)提到了好多次Bean,居然忘記了講Bean是什么。沒事,現(xiàn)在講也不晚。Java中的Bean是一種規(guī)范,是一種特殊的java類。所以我們先來看看Bean的規(guī)范。
- Bean必須生成public class類。
- 所有屬性必須封裝,Bean類的屬性都為private。
- 屬性值應(yīng)該通過一組方法(getXxx 和 setXxx)來訪問和修改。
- Bean類必須有一個(gè)空的構(gòu)造函數(shù)(默認(rèn)構(gòu)造函數(shù))。
這些就是Bean的基本規(guī)范,我們可以進(jìn)行擴(kuò)充,但是這些最基本的都是要滿足的,否則就算不上一個(gè)標(biāo)準(zhǔn)的Bean。下面我來舉一個(gè)標(biāo)準(zhǔn)的Bean的例子,注意上面的四個(gè)要素。
public class User {
private String id;
private String name;
public User(){}
public User(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
是的,Bean就是這樣的一種特殊的類,這個(gè)東西必須要牢記,因?yàn)楹竺鏁?huì)經(jīng)常用到。
二、Bean對(duì)象的三種構(gòu)造方式
自從有了spring之后,對(duì)象的構(gòu)造不用我們?cè)偃ゲ傩牧?,一切交給spring完成就行。但是,框架再高級(jí),他的底層依舊是在做一些很簡(jiǎn)單的事,這些事可能我們過去也經(jīng)常自己來做。因此,學(xué)習(xí)spring我們既要知其然也要知其所以然,下面我們就來看看有哪些構(gòu)造Bean對(duì)象的方式。順便了解一下他們的原理是什么。
1. 構(gòu)造方法構(gòu)造
這是我們用的最多的一種構(gòu)造方式。你肯定很好奇,在以前的案例中,為啥我們?cè)谂渲梦募锩鎸懸痪洌?/p>
<bean id="circle" class="com.demo.Circle"/>
然后這個(gè)Bean對(duì)象就被spring構(gòu)造出來放入IOC容器中了?其實(shí)這東西沒啥高大上的,實(shí)際上就是spring利用反射調(diào)用了這個(gè)類的默認(rèn)構(gòu)造方法而已。就是這么簡(jiǎn)單。
那么問題來了,如果我們對(duì)象中有屬性(暫時(shí)假定屬性只包含基本類型和String,對(duì)象屬性會(huì)在后面講),我們又該怎么來構(gòu)造呢?這個(gè)配置文件應(yīng)該怎么去寫呢?很簡(jiǎn)單,每個(gè)Bean對(duì)象中都由setter方法(不記得的可以去復(fù)習(xí)一下上面寫的的Bean的結(jié)構(gòu)),框架可以通過調(diào)用setter方法,把需要的值傳遞給屬性,這就是屬性注入。比如我們?cè)囍鴺?gòu)造一個(gè)本文開頭提到了一個(gè)Bean的對(duì)象:
<bean id="user" class="com.beans.User">
<property name="id" value="666"></property>
<property name="name" value="666"></property>
</bean>
這段配置主要干了什么事呢?首先,就像往常一樣,調(diào)用默認(rèn)構(gòu)造方法,構(gòu)造出一個(gè)對(duì)象,然后調(diào)用兩個(gè)setter方法,分別給兩個(gè)屬性賦值。用傳統(tǒng)代碼表示就是這樣:
User user = new User();
user.setId("666");
user.setName("666");
當(dāng)然,平時(shí)我們自己很少這么做,如果需要給屬性賦值,我們可以直接重載構(gòu)造方法,通過傳參的方式在創(chuàng)建對(duì)象的同時(shí)直接給屬性賦值,這樣可以減少代碼量。下面我們來看看怎樣讓框架調(diào)用Bean的有參構(gòu)造方法:
<bean id="user" class="com.beans.User" >
<constructor-arg name="id" value="666"></constructor-arg>
<constructor-arg name="name" value="666"></constructor-arg>
</bean>
這段配置就相當(dāng)于是直接對(duì)有參構(gòu)造方法進(jìn)行調(diào)用,這里的arg是arguement(參數(shù))的縮寫,所以constructor-arg就是構(gòu)造方法的參數(shù)列表的意思,name很顯然就是參數(shù)名了,value就是我們要填入的參數(shù)值。所以我們就要為參數(shù)列表中所有的參數(shù)進(jìn)行填值,就像我們過去做的一樣:
User user = new User("666", "666");
現(xiàn)在你應(yīng)該已經(jīng)了解了spring框架究竟干了什么了吧。看似高大上的框架做的事也不過如此嘛,最基本的東西是永遠(yuǎn)逃不掉的。
2. 靜態(tài)工廠構(gòu)造
這種方式一般只有特定場(chǎng)景會(huì)使用,所以我們就簡(jiǎn)單看看就行。這里的靜態(tài)工廠和我們前面講的工廠模式差不多,首先我們要有一個(gè)工廠:
public class UserFactory {
public static User createPerson(){
return new User();
}
public static User createPerson(Integer id,String name){
return new User(id,name);
}
}
下面我們來看看配置文件如何書寫:
<bean id="user" class="com.beans.factory.UserFactory" factory-method="createPerson">
<constructor-arg name="id" value="666"></constructor-arg>
<constructor-arg name="name" value="666"></constructor-arg>
</bean>
使用靜態(tài)工廠方法創(chuàng)建Bean實(shí)例需要為<bean />元素指定除id外如下屬性:
- class:指定靜態(tài)工廠的全類名(相當(dāng)于指定工廠的地址)
- factory-method:指定由靜態(tài)工廠的哪個(gè)方法創(chuàng)建該Bean實(shí)例(指定由工廠的哪個(gè)車間創(chuàng)建Bean)
- 方法參數(shù):如果靜態(tài)工廠方法需要參數(shù),則使用<constructor-arg />元素傳入。
3. 實(shí)例工廠構(gòu)造
實(shí)例工廠和靜態(tài)工廠唯一的區(qū)別就是我們需要先實(shí)例化工廠對(duì)象,才能構(gòu)造我們需要的Bean對(duì)象:
<!-- 先構(gòu)造工廠對(duì)象,class指定該工廠的實(shí)現(xiàn)類,工廠對(duì)象負(fù)責(zé)產(chǎn)生其他Bean實(shí)例 -->
<bean id="userFactory" class="com.beans.factory.UserFactory"/>
<!-- 再引用工廠對(duì)象來配置其他Bean -->
<bean id="user" factory-bean="userFactory" factory-method="createPerson">
<constructor-arg name="id" value="666"></constructor-arg>
<constructor-arg name="name" value="666"></constructor-arg>
</bean>
調(diào)用實(shí)例化工廠需要為<bean />指定除id外如下屬性:
- factory-bean :該屬性指定工廠Bean的id
- factory-method:該屬性指定實(shí)例工廠的工廠方法。
- 方法參數(shù):如果靜態(tài)工廠方法需要參數(shù),則使用<constructor-arg />元素傳入。
實(shí)例工廠和靜態(tài)工廠平時(shí)用的不算特別多,平時(shí)開發(fā)時(shí)用的最多的還是最開始說的構(gòu)造方法構(gòu)造?,F(xiàn)在有個(gè)重要的問題,如果Bean對(duì)象的屬性是一個(gè)對(duì)象呢?這就是我們下面要講的——依賴注入(DI)
三、依賴注入
講spring IOC,不能不講DI,這兩個(gè)東西基本上可以說是相輔相成、唇亡齒寒的,所以現(xiàn)在我們就來看看IOC和DI的關(guān)系??刂品崔D(zhuǎn)——IOC(Inversion of Control)的意思是創(chuàng)建對(duì)象的控制權(quán)進(jìn)行轉(zhuǎn)移。以前創(chuàng)建對(duì)象的主動(dòng)權(quán)和創(chuàng)建時(shí)機(jī)是由自己把控,而現(xiàn)在這種權(quán)力轉(zhuǎn)移到了spring。
IOC的最重要的一個(gè)功能是在系統(tǒng)運(yùn)行中,動(dòng)態(tài)的向某個(gè)對(duì)象提供它所需要的其他對(duì)象作為屬性。這一點(diǎn)是通過DI(Dependency Injection,依賴注入)來實(shí)現(xiàn)的。下面我來舉一個(gè)依賴注入的例子:
比如A類需要使用JBDC,以前我們要在A類中編寫代碼來自己new一個(gè)Connection對(duì)象(這邊咱先不考慮數(shù)據(jù)庫連接池)?,F(xiàn)在有了 spring,我們就只需要用配置文件告訴spring,A類中需要一個(gè)Connection對(duì)象,至于這個(gè)Connection怎么構(gòu)造,何時(shí)構(gòu)造,A類不需要知道。在運(yùn)行時(shí),spring會(huì)在適當(dāng)?shù)臅r(shí)候構(gòu)造一個(gè)Connection對(duì)象,然后注入到A類當(dāng)中,這樣就完成了對(duì)各個(gè)對(duì)象之間關(guān)系的控制。A類需要依賴Connection這個(gè)屬性才能正常運(yùn)行,而這個(gè)Connection對(duì)象是由spring注入到A中的,依賴注入的名字就這么來的。
看了這么多,是不是發(fā)現(xiàn)其實(shí)依賴注入和我們上文講的屬性注入其實(shí)是同一類東西,都是動(dòng)態(tài)地給對(duì)象的屬性賦值,只不過這里的屬性是一個(gè)對(duì)象,上文講的的屬性是簡(jiǎn)單類型而已。依賴注入聽起來很高端,實(shí)際上就是給對(duì)象的對(duì)象屬性賦值而已。
四、Bean的生命周期
講了如何編寫配置文件,那么你一定好奇在spring內(nèi)部每時(shí)每刻這個(gè)Bean的狀態(tài)應(yīng)該是怎么樣的。于是我們就來看看spring中Bean的生命周期。不過,有一點(diǎn)要提前說明,Spring只幫我們管理單例模式(singleton)Bean的完整生命周期,對(duì)于 多例模式(prototype)的Bean,Spring 在創(chuàng)建好交給使用者之后則不會(huì)再管理后續(xù)的生命周期了。單例模式和多例模式放在后面講。

來看看我們的Bean對(duì)象是如何產(chǎn)生的。這個(gè)圖乍一看很嚇人,其實(shí)里面許多東西都是一些擴(kuò)展點(diǎn),他們穿插于Bean的生命周期中,我們一開始不需要去折騰這些擴(kuò)展點(diǎn),我們的關(guān)注點(diǎn)應(yīng)該在Bean的生命周期本身。
其實(shí),Bean的生命周期一共只有四個(gè)階段,分別是:
- 實(shí)例化 Instantiation
- 屬性賦值 Populate
- 初始化 Initialization
- 銷毀 Destruction
要徹底搞清楚Spring的生命周期,首先要把這四個(gè)階段牢牢記住。實(shí)例化和屬性賦值對(duì)應(yīng)構(gòu)造方法和setter方法的注入,初始化和銷毀是用戶能自定義擴(kuò)展的兩個(gè)階段,我們可以自己在這兩個(gè)函數(shù)里面書寫我們需要的邏輯。在這四個(gè)階段之間穿插的各種擴(kuò)展點(diǎn),以后再講。
首先我們先來看前三個(gè),他們的主要邏輯都在doCreateBean方法中,順序調(diào)用三個(gè)方法,這三個(gè)方法與三個(gè)生命周期階段一一對(duì)應(yīng):
//PS:下面的代碼已經(jīng)刪去暫時(shí)不用了解的部分,只留下核心部分
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 實(shí)例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 屬性賦值
populateBean(beanName, mbd, instanceWrapper);
// 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
有了這三個(gè),再加上一個(gè)銷毀,組成了Bean的最重要的四個(gè)生命周期階段。如下圖所示,當(dāng)然,下圖除了這四個(gè)基本的生命周期階段之外,還加上了一些擴(kuò)展點(diǎn):

初學(xué)時(shí)我們只需要知道這些擴(kuò)展點(diǎn)的存在即可,至于他們具體該怎么用,后面用到的時(shí)候會(huì)講。我們?cè)谶@里需要明白的是四個(gè)最基本的生命周期。其他的擴(kuò)展點(diǎn)平時(shí)開發(fā)很少用到,但是在讀一些Java中間件源碼的時(shí)候,這些擴(kuò)展點(diǎn)就必須得弄明白了。
Ioc中Bean的作用域
在Spring中,可以在< bean >元素的scope屬性里設(shè)置bean的作用域,以決定這個(gè)bean是單實(shí)例的還是多實(shí)例的。
默認(rèn)情況下,Spring值為每個(gè)在IOC容器里聲明的bean創(chuàng)建唯一一個(gè)實(shí)例,整個(gè)Ioc容器范圍內(nèi)都能共享該實(shí)例:所有后續(xù)的getBean()調(diào)用和bean引用都將返回這個(gè)唯一的bean。該作用域被稱為singleton,它是所有bean的默認(rèn)作用域。

當(dāng)bean的作用域?yàn)閱卫龝r(shí),Spring會(huì)在IOC容器對(duì)象創(chuàng)建時(shí)就創(chuàng)建bean的對(duì)象實(shí)例。而當(dāng)bean的作用域?yàn)閜rototype時(shí),Ioc容器在獲取bean的實(shí)例時(shí)創(chuàng)建bean的實(shí)例對(duì)象

bean的作用范圍和生命周期
單例對(duì)象: scope=“singleton”
- 一個(gè)應(yīng)用只有一個(gè)對(duì)象的實(shí)例。他的作用范圍就是整個(gè)引用
- 生命周期:
- 對(duì)象出生:當(dāng)應(yīng)用加載,創(chuàng)建容器時(shí),對(duì)象創(chuàng)建
- 對(duì)象活著:只要容器在,對(duì)象一直活著
- 對(duì)象死亡:當(dāng)應(yīng)用卸載,銷毀容器時(shí),對(duì)象銷毀
多例對(duì)象:scope=“prototype”
- 每次訪問對(duì)象時(shí),都會(huì)重新創(chuàng)建對(duì)象實(shí)例
- 生命周期:
- 對(duì)象出生:當(dāng)使用對(duì)象時(shí),創(chuàng)建新的對(duì)象實(shí)例
- 對(duì)象活著:只要對(duì)象在使用中,就一直活著
- 對(duì)象死亡:當(dāng)對(duì)象長(zhǎng)時(shí)間不用時(shí),就被java的垃圾回收器回收了。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring實(shí)現(xiàn)上拉刷新和下拉加載效果
這篇文章主要為大家詳細(xì)介紹了Spring實(shí)現(xiàn)上拉刷新和下拉加載效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
java實(shí)現(xiàn)解析json復(fù)雜數(shù)據(jù)的方法詳解
這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)解析json復(fù)雜數(shù)據(jù),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以學(xué)習(xí)一下2024-01-01
詳解Spring緩存注解@Cacheable,@CachePut , @CacheEvict使用
這篇文章主要介紹了詳解Spring緩存注解@Cacheable,@CachePut , @CacheEvict使用,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05
詳解Java函數(shù)式編程和lambda表達(dá)式
這篇文章主要介紹了Java函數(shù)式編程和lambda表達(dá)式,對(duì)lambda感興趣的同學(xué),一定要看一下2021-04-04
詳解SpringBoot中RestTemplate的幾種實(shí)現(xiàn)
這篇文章主要介紹了詳解SpringBoot中RestTemplate的幾種實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11

