第 2 章 Spring中的Bean

在第一章中,詳細講解了Spring的loC思想及原理,并通過案例演示了Spring框架的基本使用。本章將在第一章節(jié)的基礎(chǔ)上,針對Spring中Bean的相關(guān)知識進行詳細的講解遣总。

Bean的配置
  • Spring可以被看作是一個大型工廠,這個工廠的作用就是生產(chǎn)和管理Spring容器中的Bean轨功。如果想要在項目中使用這個工廠旭斥,就需要開發(fā)者對Spring的配置文件進行配置。
  • Spring器支持XML和Properties兩種格式的配置文件古涧,在實際開發(fā)中垂券,最常使用的就是XML格式的配置方式。這種配置方式通過XML文件來注冊并管理Bean之間的依賴關(guān)系羡滑。接下來本小節(jié)將使用XML文件的形式對Bean的屬性和定義進行詳細的講解菇爪。
  • 在Spring中,XML配置文件的根元素是<beans>柒昏,<beans>中包含了多個<bean>子元素凳宙,每一個<bean>子元素定義了一個Bean,并描述了該Bean如何被裝配到Spring容器中职祷。<bean>元素中同樣包含了多個屬性以及子元素氏涩,其常用屬性及子元素如表所示。
屬性或子元素名稱 描述
id 一個Bean的唯一標識符有梆,Spring窯器對Bean的配置是尖、筐理通過該屬性來完成
name Spring容器同樣可以通過此屬性對容器中的Bean進行配置和管理,name屬性中可以為Bean指定多個名稱泥耀,每個名稱之間用逗號或分號隔開
class 該屬性指定了Bean的具體實現(xiàn)類饺汹,它必須是一個完整的類名,使用類的全限定名
scope 用來設(shè)定Bean實例的作用域爆袍,其屬性值有:singleton(單例)首繁、prototype(原型)、request陨囊、sesslon弦疮、globalSession,application和websocket.真默認值為singleton
constructor-arg <bean>元素的子元素,可以使用此元素傳入構(gòu)造參數(shù)進行實例化蜘醋。該元素的index屬性指定構(gòu)造參數(shù)的序號(從0開始),type屬性指定構(gòu)造參數(shù)的類型胁塞,參數(shù)值可以通過ref屬性或value屬性直接指定,也可以通過ref或value子元素指定
property <bean>元素的子元素压语,用于調(diào)用Bean實例中的setter方法完成屬性賦值啸罢,從而完成依賴注入。該元素的nae屬性指定Bean實例中的相應(yīng)屬性氈ref屬性或value屬性用于指定參數(shù)值
ref <property>胎食、<constructor-arg>等元素的屬性或子元素扰才,可以用于指定對Bean工廠中某個Bean實例的引用
value <property>、<constructor-arg>等元素的屬性或子元素厕怜,可以用于直接指定一個常量值
list 用于封裝List或數(shù)組類型的依賴注入
set 用于封裝Set類型的依賴注入
map 用于封裝Map類型的依賴注入
entry <map>元素的子元素衩匣,用于設(shè)置一個鍵值對。真key屬性指定字符串類型的鍵值粥航,ref或value子元素指定真值琅捏,也可以通過value-ref或value屬性指定真值

在配置文件中,通常一個普通的Bean只需要定義id(或name)和class兩個屬性即可递雀,定義Bean的方式如下所示柄延。

   <!-- 使用 id 屬性定義 bean1 ,其對應(yīng)的實現(xiàn)類為 com.neuedu.Bean1 -->
   <bean id="bean1" class="com.neuedu.Bean1"></bean>
   <!-- 使用 name 屬性定義 bean2 缀程,其對應(yīng)的實現(xiàn)類為 com.neuedu.Bean2 -->
   <bean name="bean2" class="com.neuedu.Bean2"></bean>

在上述代碼中搜吧,分別使用id屬性和name屬性定義了兩個Bean,并使用class元素指定其對應(yīng)的實現(xiàn)類杠输。
注意:在Bean中沒有指定id和name赎败,則Spring會將class當做id來使用。

Bean 的實例化

在面向?qū)ο蟮某绦蛑写兰祝胍褂媚硞€對象僵刮,就需要先實例化這個對象。同樣鹦牛,在Spring中搞糕,要想使用容器中的Bean,也需要實例化Bean曼追。實例化Bean有三種方式窍仰,分別為構(gòu)造器實例化靜態(tài)工廠方式實例化實例工廠方式實例化(其中最常用的是構(gòu)造器實例化)礼殊。接下來的幾個小節(jié)中驹吮,將分別對這三種實例化Bean的方式進行詳細講解针史。

  • 構(gòu)造器實例化

構(gòu)造器實例化是指Spring容器通過Bean對應(yīng)類中默認的無參構(gòu)造方法來實例化Bean。下面通過一個案例來演示Spring容器是如何通過構(gòu)造器來實例化Bean的碟狞。
(1)在Eclipse中啄枕,創(chuàng)建一個名為spring02的Web項目,在該項目的lib目錄中加入Spring支持和依賴的JAR包族沃。
(2)在spring02項目的src目錄下频祝,創(chuàng)建一個com.neuedu.instance.constructor包,在該包中創(chuàng)建Bean1類脆淹,如下面文件所示常空。

package com.neuedu.instance.constructor;
public class Bean1 {
  
}

(3)在com.neuedu.instance.constructor包中,創(chuàng)建Spring的配置文件beans1.xml盖溺,在配置文件中定義一個id為bean1的Bean漓糙,并通過class屬性指定其對應(yīng)的實現(xiàn)類為Bean1,如下面文件所示烘嘱。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <bean id="bean1" class="com.neuedu.instance.constructor.Bean1">></bean>

</beans>

(4)在com.neuedu.instance.constructor包中兼蜈,創(chuàng)建測試類InstanceTest1,來測試構(gòu)造器是否能實例化Bean拙友,編輯后文件如下所示为狸。

package com.neuedu.instance.constructor;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InstanceTest1 {

  public static void main(String[] args) {
      //定義配置文件路徑  
      String xmlPath = "com/neuedu/instance/constructor/beans1.xml"; 
      //ApplicationContext 在加載配置文件時,對 Bean 進行實例化 
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath) ; 
      Bean1 bean = (Bean1) applicationContext .getBean("bean1"); 
      System.out.println(bean) ; 

  }

}

在上述文件中遗契,首先定義了配置文件的路徑辐棒,然后Spring容器ApplicationContext會加載配置文件。在加載時牍蜂,Spring容器會通過id為bean1的實現(xiàn)類Bean1中默認的無參構(gòu)造方法對Bean進行實例化漾根。執(zhí)行程序后,控制臺的輸出結(jié)果如圖所示鲫竞。



從圖中可以看出辐怕,Spring容器已經(jīng)成功實例化了Bean1,并輸出了結(jié)果从绘。
為了方便大家的學習寄疏,本章中的所有配置文件和類文件(包括測試類)都根據(jù)知識點放置在同一個包中。在實際開發(fā)中僵井,為了方便管理和維護陕截,建議將這些文件根據(jù)類別放置在不同目錄中。

  • 靜態(tài)工廠方式實例化

使用靜態(tài)工廠是實例化Bean的另一種方式批什。該方式要求開發(fā)者創(chuàng)建一個靜態(tài)工廠的方法來創(chuàng)建Bean的實例农曲,其Bean配中的class屬性所指定的不再是Bean實例的實現(xiàn)類,而是靜態(tài)工廠類驻债,同時還需要使用factory-method屬性來指定所創(chuàng)建的靜態(tài)工廠方法乳规。下面通過一個案例來演示如何使用靜態(tài)工廠方式實例化Bean形葬。
(1)在spring02項目的src目錄下,創(chuàng)建一個com.neuedu.instance.static_factory包暮的,在該包中創(chuàng)建一個Bean2類荷并,該類與Bean1一樣,不需添加任何方法青扔。
(2)在com.neuedu.instance.static_factory包中,創(chuàng)建一個MyBean2Factory類翩伪,并在類中創(chuàng)建一個靜態(tài)方法createBean()來返回Bean2實例微猖,文件如下所示。

package com.neuedu.instance.static_factory;
public class MyBean2Factory {
  public static Bean2 createBean(){
      return new Bean2();
  }
}

(3)在com.neuedu.instance.static_factory包中缘屹,創(chuàng)建Spring配置文件beans2.xml凛剥,編輯后文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <bean id="bean2" >class="com.neuedu.instance.static_factory.MyBean2Factory" 
   factory-method="createBean"></bean>

</beans>

在上述配置文件中轻姿,首先通過<bean>元素的id屬性定義了一個名稱為bean2的Bean犁珠,然后由于使用的是靜態(tài)工廠方法,所以需要通過class屬性指定其對應(yīng)的工廠實現(xiàn)類為MyBean2Factory互亮。由于這種方式配置Bean后犁享,Spring容器不知道哪個是所需要的工廠方法,所以增加了factory-method屬性來告訴Spring容器豹休,其方法名稱為createBean炊昆。
(4)在com.neuedu.instance.static_factory包中,創(chuàng)建一個測試類InstanceTest2威根,來測試使用靜態(tài)工廠方式是否能實例化Bean凤巨,編輯后文件如下所示。

package com.neuedu.instance.static_factory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.neuedu.instance.constructor.Bean1;

public class InstanceTest2 {

  public static void main(String[] args) {
      // 定義配置文件路徑
      String xmlPath = "com/neuedu/instance/static_factory/beans2.xml";       
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);        
      System.out.println(applicationContext.getBean("bean2"));

  }
}

在執(zhí)行程序后洛搀,控制臺的輸出結(jié)果如圖下圖所示敢茁。從圖中可以看到,使用自定義的靜態(tài)工廠方法留美,已成功實例化了Bean2彰檬。


  • 實例工廠方式實例化

還有一種實例化Bean的方式就是采用實例工廠。此種方式的工廠類中谎砾,不再使用靜態(tài)方法創(chuàng)建Bean例僧叉,而是用直接創(chuàng)建Bean實例的方式。同時棺榔,在配置文件中瓶堕,需要實例化的Bean也不是通過class屬性直接指向的實例化類,而是通過factory-bean屬性指向配置的實例工廠症歇,然后使用factory-method屬性確定使用工廠中的哪個方法郎笆。下面通過一個案例來演示實例工廠方式的使用谭梗。
(1)在spring02項目的src目錄下,創(chuàng)建一個com.neuedu.instance.factory包宛蚓,在該包中創(chuàng)建Bean3類激捏,該類與Bean1一樣,不需添加任何方法凄吏。
(2)在com.neuedu.instance.factory包中远舅,創(chuàng)建工廠類MyBean3Factory,在類中使用默認無參構(gòu)造方法輸出"bean3工廠實例化中"語句痕钢,并使用createBean()方法創(chuàng)建Bean3對象图柏,文件如下所示。

package com.neuedu.instance.factory;

public class MyBean3Factory {

  public MyBean3Factory() {
      System.out.println("bean3工廠實例化中");
  }
  //創(chuàng)建 Bean3 實例的方法 
  private Bean3 createBean() {
      return new Bean3();
  }
}

(3)在com.neuedu.instance.factory包中任连,創(chuàng)建Spring配置文件beans3.xml蚤吹,設(shè)置相關(guān)配置后,文件如下所示随抠。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <!-- 配配置工 -->
   <bean id="myBean3Factory" >class="com.neuedu.instance.factory.MyBean3Factory"></bean>
   <!-- 使用 factory-bean 屬性指向配置的實例工廠裁着, 
         使用 factory-method 屬性確定使用工廠中的哪個方法  -->
   <bean id="bean3" factory-bean="myBean3Factory" factory->method="createBean"></bean>

</beans>

在上述配置文件中,首先配置了一個工廠Bean拱她,然后配置了需要實例化的Bean二驰。id為bean3的Bean中,使用factory-bean屬性指向配置的實例工廠秉沼,該屬性值就是工廠Bean的id诸蚕。使用factory-method屬性來確定使用工廠中的createBean()方法。
(4)在com.neuedu.instance.factory的包中氧猬,創(chuàng)建測試類InstanceTest3背犯,來測試實例工廠方式能否實例化Bean,編輯后文件如下所示盅抚。

package com.neuedu.instance.factory;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InstanceTest3 {

  public static void main(String[] args) {
      // 定義配置文件路徑
      String xmlPath = "com/neuedu/instance/factory/beans3.xml";
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      System.out.println(applicationContext.getBean("bean3"));

  }
}

在執(zhí)行程序后漠魏,控制臺的輸出結(jié)果如下圖所示。



從圖中可以看到妄均,使用實例工廠的方式柱锹,同樣成功實例化了Bean3。

Bean 的作用域

通過Spring容器創(chuàng)建一個Bean的實例時丰包,不僅可以完成Bean的實例化禁熏,還可以為Bean指定特定的作用域。本節(jié)將主要圍繞Bean的作用域知識進行講解邑彪。

  • 作用域的種類

Spring4.3中為Bean的實例定義了7種作用域瞧毙,這7種作用域及其說明如表所示。

作用域名稱 描述
singleton 使用singleton定義的Bean在Spring容器中將只有一個實例,也就是說宙彪,無論有多少個Bean引用它矩动,始終將指向同一個對象。這也是Spring容器默認的作用域释漆。
prototype 每次通過Spring容器獲取的prototype定義的Bean時悲没,容器都將創(chuàng)建一個新的Bean實例
request 在一次HTTP請求中,容器會返回該Bean的同一個實例男图。不同的HTTP請求則會產(chǎn)生一個新的Bean示姿,而且該Bean在當前HTTPRequest內(nèi)有效
sesslon 在一次HTTPSession中,容器會返回該Bean的同一個實例逊笆。對不同的HTTP請求則會產(chǎn)生一個新的Bean栈戳,而且該Bean僅在當前HTTPSession內(nèi)有效
globalSession 在一個全局的HTTPSession中,容器會返回該Bean的同一個實例览露。僅在使用portlet上下文時有效
application 在每個ServletContext對象創(chuàng)建一個實例。僅在Web相關(guān)的ApplicationContext中生效
websocket 在每個websocket對象創(chuàng)建一個實例譬胎。僅在Web相關(guān)的ApplicationContext中生效

在表的7種作用域中差牛,singleton和prototype是最常用的兩種,在接下來的兩個小節(jié)中堰乔,將會對這兩種作用域進行詳細的講解偏化。

  • singleton 作用域

singleton是Spring容器默認的作用域,當Bean的作用域為singleton時镐侯,Spring容器就只會存在一個共享的Bean實例侦讨,并且所有對Bean的請求,只要id與該Bean的id屬性相匹配苟翻,就會返回同一個Bean實例韵卤。singleton作用域?qū)τ跓o會話狀態(tài)的Bean(如Dao組件、Service組件)來說崇猫,是最理想的選擇沈条。
在Spring配置文件中,Bean的作用域是通過<bean>元素的scope屬性來指定的诅炉,該屬性值可以設(shè)置為singleton蜡歹、prototype、request涕烧、session月而,globalSession、application和websocket七個值议纯,分別表示七種作用域父款。要將作用域定義成singleton,只需將scope的屬性值設(shè)置為singleton即可,其示例代碼如下铛漓。

<bean id="scope" class="com.neuedu.scope.Scope" scope="singleton"></bean>

在項目spring02中溯香,創(chuàng)建一個com.neuedu.scope包,在包中創(chuàng)建Scope類浓恶,該類不需要寫任何方法玫坛。然后在該包中創(chuàng)建一個配置文件beans4.xml,將上述示例代碼寫入配置文件中包晰。最后在包中創(chuàng)建測試類ScopeTest湿镀,來測試singleton作用域,編輯后文件如下所示伐憾。

package com.neuedu.scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ScopeTest {

  public static void main(String[] args) {
      //定義配置文件路徑
      String xmlPath = "com/neuedu/scope/beans4.xml";
      //加載配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      //輸出獲得實例
      System.out.println(applicationContext.getBean("scope"));
      System.out.println(applicationContext.getBean("scope"));
  }
}

在行程序后勉痴,控制臺的輸出結(jié)果如圖所示。



從圖中可以看出树肃,兩次輸出的結(jié)果相同蒸矛,這說明Spring容器只創(chuàng)建了一個Scope類的實例。需要注意的是胸嘴,如果不設(shè)置scope="singleton"雏掠,其輸出結(jié)果也是一個實例,因為Spring容器默認的作用域就是singleton劣像。

  • prototype 作用域

對需要保持會話狀態(tài)的Bean(如Struts2的Action類)應(yīng)該使用prototype作用域乡话。在使用prototype作用域時,Spring容器會為每個對該Bean的請求都創(chuàng)建一個新的實例耳奕。
要將Bean定義為prototype作用域绑青,只需在配置文件中將<bean>元素的scope屬性值設(shè)置prototype即可其示例代碼如下。

<bean id="scope" class="com.neuedu.scope.Scope" scope="prototype"></bean>

將上一個小節(jié)中的配置文件更改成上述代碼形式后屋群,再次運行測試類ScopeTest闸婴,控制臺的輸出結(jié)果如圖所示。



從圖中可以看到芍躏,兩次輸出的Bean實例并不相同掠拳,這說明在prototype作用域下,創(chuàng)建了兩個不同的Scope實例纸肉。

Bean 的生命周期

Spring容器可以管理singleton作用域的Bean的生命周期溺欧,在此作用域下,Spring能精確地知道該Bean何時被創(chuàng)建柏肪,何時初始化完成以及何時被銷毀姐刁。對于prototype作用域的Bean,Spring只負責創(chuàng)建,當容器創(chuàng)建了Bean實例后烦味,Bean的實例就交給客戶端代碼來管理聂使,Spring容器將不再跟蹤其生命周期壁拉。每次客戶端請求prototype作用域的Bean時,Spring容器都會創(chuàng)建一個新的實例柏靶,并且不會管那些被配置成prototype作用域的Bean的生命周期弃理。
了解Bean的生命周期的意義就在于,可以在某個Bean生命周期的某些指定時刻完成一些相關(guān)操作屎蜓。這種時刻可能有很多痘昌,但在一般情況下,常會在Bean的postinitiation(初始化后)和predestruction(銷毀前)執(zhí)行一些相關(guān)操作炬转。
在Spring中辆苔,Bean生命周期的執(zhí)行是一個很復雜的過程,讀者可以利用Spring提供的方法來定制Bean創(chuàng)建過程扼劈。當一個Bean被加載到Spring容器時驻啤,它就具有了生命,而Spring容器在保證一個Bean能夠使用之前荐吵,會做很多工作骑冗。Spring容器中,Bean的生命周期流程如下圖所示先煎。



在上圖中贼涩,Bean的生命周期的整個執(zhí)行過程描述如下。

  • (1)根據(jù)配置情況調(diào)用Bean構(gòu)造方法或工廠方法實例化Bean榨婆。
  • (2)利用依賴注入完成Bean中所有屬性值的配置注入磁携。
  • (3)如果Bean實現(xiàn)BeanNameAware接口褒侧,則Spring調(diào)用Bean的setBeanName()方法傳入當前Bean的id值良风。
  • (4)如果Bean實現(xiàn)了BeanFactoryAware接口,則Spring調(diào)用setBeanFactory()方法傳入當前工廠實例的引用闷供。
  • (5)如果Bean實現(xiàn)了ApplicationContextAware接口烟央,貝IJSpring調(diào)用setApplicationContext()方法傳入當前ApplicationContext實例的引用。
  • (6)如果BeanPostProcessor和Bean關(guān)聯(lián)歪脏,則Spring將調(diào)用該接口的預初始化方法postProcessBeforelnitialzation()對Bean進行加工操作疑俭,這個非常重要,Spring的AOP就是用它實現(xiàn)的婿失。
  • (7)如果Bean實現(xiàn)了InitializingBean接口钞艇,則Spring將調(diào)用afterPropertiesSet()方法。
  • (8)如果在配置文件中通過init-method屬性指定了初始化方法豪硅,則調(diào)用該初始化方法哩照。
  • (9)如果有BeanPostProcessor和Bean關(guān)聯(lián),貝IJSpring將調(diào)用該接口的初始化方法postProcessAfterlnitialization()懒浮。此時飘弧,Bean已經(jīng)可以被應(yīng)用系統(tǒng)使用了。
  • (10)如果在<bean>中指定了該Bean的作用范圍為scope="singleton",則將該Bean放入SpringloC的緩存池中次伶,將觸發(fā)Spring對該Bean的生命周期管理;如果在<bean>中指定了該Bean的作用范圍為scope="prototype"痴昧,則將該Bean交給調(diào)用者,調(diào)用者管理該Bean的生命周期冠王,Spring不再管理該Bean赶撰。
  • (11)如果Bean實現(xiàn)DisposableBean接口,則Spring會調(diào)用destory()方法將Spring中的Bean銷毀;如果在配置文件中通過destory-method屬性指定了Bean的銷毀方法版确,則Spring將調(diào)用該方法進行銷毀扣囊。
    Spring為Bean提供了細致全面的生命周期過程,通過實現(xiàn)特定的接口或通過<bean>的屬性設(shè)置绒疗,都可以對Bean的生命周期過程產(chǎn)生影響侵歇。我們可以隨意地配置<bean>的屬性,但是在這里建議不要過多地使用Bean實現(xiàn)接口吓蘑,因為這樣會使代碼和Spring聚合比較緊密惕虑。
Bean 的裝配方式

Bean的裝配可以理解為依賴關(guān)系注入,Bean的裝配方式即Bean依賴注入的方式磨镶。Spring容器支持多種形式的Bean的裝配方式溃蔫,如基于XML的裝配基于注解(Annotation)的裝配自動裝配等(其中最常用的是基于注解的裝配)琳猫。本節(jié)將主要講解這三種裝配方式的使用伟叛。

  • 基于 XML 的裝配

Spring提供了兩種基于XML的裝配方式:設(shè)值注入(SetterInjection)和構(gòu)造注入(ConstructorInjection)。下面就講解下如何在XML配置文件中使用這兩種注入方式來實現(xiàn)基于XML的裝配脐嫂。
在Spring實例化Bean的過程中统刮,Spring首先會調(diào)用Bean的默認構(gòu)造方法來實例化Bean對象,然后通過反射的方式調(diào)用setter方法來注入屬性值账千。因此侥蒙,設(shè)值注入要求一個Bean必須滿足以下兩點要求。

  • Bean類必須提供一個默認的無參構(gòu)造方法匀奏。
  • Bean類必須為需要注入的屬性提供對應(yīng)的setter方法鞭衩。

使用設(shè)值注入時,在Spring配置文件中娃善,需要使用<bean>元素的子元素<property>來為每個屬性注入值;而使用構(gòu)造注入時论衍,在配置文件里,需要使用<bean>元素的子元素<constructor-arg>來定義構(gòu)造方法的參數(shù)聚磺,可以使用其value屬性(或子元素)來設(shè)置該參數(shù)的值坯台。下面通過一個案例來演示基于XML方式的Bean的裝配。
(1)在項目spring02的src目錄下咧最,創(chuàng)建一個com.neuedu.assemble包捂人,在該包中創(chuàng)建User類御雕,并在類中定義username、password和list集合三個屬性及其對應(yīng)的setter方法滥搭,文件如下所示酸纲。

package com.neuedu.assemble;
import java.util.List;

public class User {
  private String username;
  private Integer password;
  private List<String> list;
  
  
  /**
   * 1.使用構(gòu)造注入 
   * 1.1 提供帶所有參數(shù)的有參構(gòu)造方法。 
   */ 
  public User(String username, Integer password, List<String> list) {
      super();
      this.username = username;
      this.password = password;
      this.list = list;
  }
  
  /**
   *  2.使用設(shè)值注入 
   *  2.1 提供默認空參構(gòu)造方法; 
   *  2.2 為所有屬性提供 setter 方法瑟匆。 
   */
  public User() {
      super();
      // TODO Auto-generated constructor stub
  }
  public void setUsername(String username) {
      this.username = username;
  }
  public void setPassword(Integer password) {
      this.password = password;
  }
  public void setList(List<String> list) {
      this.list = list;
  }
  
  @Override
  public String toString() {
      return "User [username=" + username + ", password=" + password >+ ", list=" + list + "]";
  }
}

在上述文件中闽坡,由于要使用構(gòu)造注入,所以需要其有參和無參的構(gòu)造方法愁溜。同時疾嗅,為了輸出時能夠看到結(jié)果,還重寫了其屬性的toString()方法冕象。
(2)在com.neuedu.assemble包中代承,創(chuàng)建配置文件beans5.xml,在配置文件中通過構(gòu)造注入和設(shè)值注入的方式裝配User類的實例渐扮,文件如下所示论悴。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
   <!-- 1.使用構(gòu)造注入方式裝配 User 實例 -->
   <bean id="user1" class="com.neuedu.assemble.User">
      <constructor-arg index="0" value="zhaosi"></constructor-arg>
      <constructor-arg index="1" value="456"></constructor-arg>
      <constructor-arg index="2">
          <list>
              <value>"constructorvalue1"</value>
              <value>"constructorvalue2"</value>
          </list>
      </constructor-arg>
   </bean>
   <!-- 2.使用設(shè)值注入方式裝配 User 實例  -->
   <bean id="user2" class="com.neuedu.assemble.User">
      <property name="username" value="liuneng"></property>
      <property name="password" value="123"></property>
      <!-- 注入list集合  -->
      <property name="list">
          <list>
              <value>"setlistvalue1"</value>
              <value>"setlistvalue2"</value>
          </list>
      </property>
   </bean>

</beans>

在上述配置文件中,<constructor-arg>元素用于定義構(gòu)造方法的參數(shù)墓律,其屬性index表示其索引(從0開始)膀估,value屬性用于設(shè)置注入的值,其子元素<Iist>來為User類中對應(yīng)的list集合屬性注入值耻讽。然后又使用了設(shè)值注入方式裝配User類的實例察纯,其中<property>元素用于調(diào)用Bean實例中的setter方法完成屬性賦值蜂筹,從而完成依賴注入甜攀,而其子元素<Iist>同樣是為User類中對應(yīng)的list集屬性注入值。
(3)在com.neuedu.assemble包中撩嚼,創(chuàng)建測試類XmlBeanAssembleTest祖驱,在類中分別獲取并輸出配置文件中的user1和user2實例握恳,文件如下所示瞒窒。

package com.neuedu.assemble;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XmlBeanAssembleTest {

  public static void main(String[] args) {
      // 定義配置文件路徑
      String xmlPath = "com/neuedu/assemble/beans5.xml";
      // 加載配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      // 構(gòu)造方式輸出結(jié)果 
      System.out.println(applicationContext.getBean("user1"));
      // 設(shè)值方式輸出結(jié)果 
      System.out.println(applicationContext.getBean("user2"));

  }
}

在行程序后捺僻,控制臺的輸出結(jié)果如圖所示。



從圖可以看出崇裁,已經(jīng)成功地使用基于XML裝配的構(gòu)造注入和設(shè)值注入兩種方式裝配了User實例匕坯。

  • 基于 Annotation 的裝配

Spring中,盡管使用XML配置文件可以實現(xiàn)Bean的裝配工作拔稳,但如果應(yīng)用中有很多Bean時葛峻,會導致XML配置文件過于臃腫,給后續(xù)的維護和升級工作帶來一定的困難巴比。為此术奖,Spring提供了對Annotation(注解)技術(shù)的全面支持礁遵。
Spring中定義了一系列的注解,常用的注解如下所示采记。

  • @Component:可以使用此注解描述Spring中的Bean佣耐,但它是一個泛化的概念,僅僅表示一個組件(Bean)唧龄,并且可以作用在任何層次兼砖。使用時只需將該注解標注在相應(yīng)類上即可。
  • @Repository:用于將數(shù)據(jù)訪問層(DAO層)的類標識為Spring中的Bean既棺,其功能與@Component相同讽挟。
  • @Service:通常作用在業(yè)務(wù)層(Service層),用于將業(yè)務(wù)層的類標識為Spring中的Bean丸冕,其功能與@Component相同耽梅。
  • @Controller:通常作用在控制層(如SpringMVC的Controller),用于將控制層的類標識為Spring中的Bean胖烛,其功能與@Component相同褐墅。
  • @Autowired:用于對Bean的屬性變量、屬性的setter方法及構(gòu)造方法進行標注洪己,配合對應(yīng)的注解處理器完成Bean的自動配置工作妥凳。默認按照Bean的類型進行裝配。
  • @Resource:其作用與Autowired一樣答捕。其區(qū)別在于@Autowired默認按照Bean類型裝配逝钥,而@Resource默認按照Bean實例名稱進行裝配。@Resource中有兩個重要屬性:name和typeoSpring將name屬性解析為Bean實例名稱拱镐,type屬性解析為Bean實例類型艘款。如果指定name屬性,則按實例名稱進行裝配;如果指定type屬性沃琅,則按Bean類型進行裝配;如果都不指定哗咆,則先按Bean實例名稱裝配,如果不能匹配益眉,再按照Bean類型進行裝自己;如果都無法匹配晌柬,則拋出NoSuchBeanDefinitionException異常。
  • @Qualifier:與@Autowired注解配合使用郭脂,會將默認的按Bean類型裝配修改為接Bean的實例名稱裝配年碘,Bean的實例名稱由@Qualifier注解的參數(shù)指定。

在上面幾個注解中展鸡,雖然@Repository屿衅、@Service與@Controller功能與@Component注解的功能相同,但為了使標注類本身用途更加清晰莹弊,建議在實際開發(fā)中使用@Repository涤久、@Service與@Controller分別對實現(xiàn)類進行標注涡尘。面,通過一個案例來演示如何通過這些注解來裝配Bean响迂。
(1)在spring02項目的src目錄下悟衩,創(chuàng)建一個com.neuedu.annotation包,在該包中創(chuàng)建接口UserDao栓拜,并在接口中定義一個save()方法座泳,文件如下所示。

package com.neuedu.annotation;
public interface UserDao {
  public void save();
}

(2)在com.neuedu.annotation包中幕与,創(chuàng)建UserDao接口的實現(xiàn)類UserDaolmpl挑势,該類需要實現(xiàn)接口中的save()方法,文件如下所示啦鸣。

package com.neuedu.annotation;
@Repository("userDao")
public class UserDaolmpl implements UserDao {

  @Override
  public void save() {
      System.out.println("userdao.....save......");

  }
}

在上述文件中潮饱,首先使用@Repository注解將UserDaolmpl類標識為Spring中的Bean,其寫法相當于配置文件中<bean id="userDao"class="com.neuedu.annotation.UserDaolmpl"/>的編寫诫给。然后在save()法中輸出打印一句話香拉,用于驗證是否成功調(diào)用了該方法。
(3)在com.neuedu.annotation包中中狂,創(chuàng)建接口UserService凫碌,在接口中同樣定義一個save()方法,文件如下所示胃榕。

package com.neuedu.annotation;
public interface UserService {
  public void save();
}

(4)在com.neuedu.annotation包中盛险,創(chuàng)建UserService接口的實現(xiàn)類UserServicelmpl,該類需要實現(xiàn)接口中的save()方法如文件所示勋又。

package com.neuedu.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService{
  @Resource(name="userDao")
  private UserDao userDao;
  @Override
  public void save() {
      //調(diào)用UserDao中的save()方法
      this.userDao.save();
      System.out.println("userservice......save......");  
  }
}

在上述文件中苦掘,首先使用@Service注解將UserServicelmpl類標識為Spring中的Bean,這相當于配置文件中<bean id="userService"class="com.neuedu.annotation.UserServicelmpl"/>的編寫;然后使用@Resource注解標注在屬性userDao上楔壤,這相當于配置文件中<propertyname="userDao"ref="userDao"/>的寫法;最后在該類的save()方法中調(diào)用userDao中的save()方法鹤啡,并輸出一句話。
(5)在com.neuedu.annotation包中蹲嚣,創(chuàng)建控制器類UserController递瑰,編輯后文件如下所示。

package com.neuedu.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;

@Controller("userController")
public class UserController {
  @Resource(name="userService")
  private UserService userService;
  public void save(){
      this.userService.save();
      System.out.println("usercontroller......save......");
  }

}

在上述文件中端铛,首先使@Controller注解標注了UserController類泣矛,這相當于在配置文件中編寫<bean id="userController"class="com.neuedu.annotation.UserController"/>;然后使用了@Resource注解標注在userService屬性上疲眷,這相當于在配置文件中編寫<propertyname="userService"ref="userService"/>;最后在其save()方法中調(diào)用了userService中的save()方法禾蚕,并輸出一句話。
(6)在com.neuedu.annotation包中狂丝,創(chuàng)建配置文件beans6.xml换淆,在配置文件中編寫基于Annotation裝配的代碼哗总,文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   <!-- 使用 context 命名空間倍试,在配置文件中開啟相應(yīng)的注解處理器 -->
   <context:annotation-config />
   <!-- 分別定義 3 個 Bean 實例  -->
   <bean id="userDao" class="com.neuedu.annotation.UserDaolmpl">></bean>
   <bean id="userService" >class="com.neuedu.annotation.UserServiceImpl"></bean>
   <bean id="userController" >class="com.neuedu.annotation.UserController"></bean>
   
</beans>

從上述代碼可以看出讯屈,文件與之前的配置文件有很大不同。首先县习,在<beans>元素中涮母,增加了4行,第7行和第8行中包含有context的約束信息;然后通過配置<context:annotation-config/>來開啟注解處理器;最后分別定義了3個Bean對應(yīng)所編寫的3個實例躁愿。與XML裝備方式有所不同的是叛本,這里不再需要配置子元素<property>。
上述Spring配置文件中的注解方式雖然較大程度簡化了XML文件中Bean的配置彤钟,但仍需要在Spring配置文件中一一配置相應(yīng)的Bean来候,為此Spring注解提供了另外一種高效的注解配置方式(對包路徑下的所有Bean文件進行掃描),其配置方式如下逸雹。

<context :cornponent-scan base-package="Bean 所在的包路栓" />

以可以將上述文件中代碼進行如下替換(推薦)营搅。

<context :cornponent-scan base-package="com.neuedu.annotation" /> 

注意:
Spring4.0以上版本使用上面的代碼對指定包中的注解進行掃描前,需要先向項目中導入SpringAOP的JARspring-aop-4.3.6.RELEASE.jar梆砸,否則程序在運行時會報出
"java.lang.NoClassDefFoundError:orglspringframeworklaop/TargetSource"錯誤转质。

(7)在com.neuedu.annotation包中,創(chuàng)建測試類AnnotationAssembleTest帖世,在類中編寫測試方法并定義配置文件的路徑峭拘,然后通過Spring容器加載配置文件并獲取UserController實例,最后調(diào)用實例中的save()方法狮暑,文件如下所示鸡挠。

package com.neuedu.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationAssembleTest {

  public static void main(String[] args) {
      // 定義配置文件路徑
      String xmlPath = "com/neuedu/annotation/beans6.xml";
      // 加載配置文件
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
      // 獲取UserController實例
      UserController userController = (UserController) applicationContext.getBean("userController");
      // 調(diào)用 UserController中的save()方法
      userController.save();
  }
}

在執(zhí)行程序后,控制臺的輸出結(jié)果如圖所示搬男。


從上圖可以看到拣展,Spring容器已成功獲取了UserController的實例,并通過調(diào)用實例中的方法執(zhí)行了各層中的輸出語句缔逛,這說明已成功實現(xiàn)了基于Annotation裝配Bean备埃。
小提示:上述案例中如果使用@Autowired注解替換@Resource注解,也可以達到同樣的效果褐奴。

  • 自動裝配

雖然使用注解的方式裝配 Bean 按脚,在一定程度上減少了配置文件中的代碼量,但是也有企業(yè)項目中敦冬, 是沒有使用注解方式開發(fā)的辅搬,那么有沒有什么辦法既可以減少代碼量,又能夠?qū)崿F(xiàn) Bean 的裝配呢?
答案是肯定的脖旱, Spring 的 <bean>元素中包含一個 autowire 屬性堪遂,我們可以通過設(shè)置 autowire 的屬性值來自動裝配 Bean介蛉。 所謂自動裝配,就是將一個 Bean 自動地注入到其他 Bean 的 Property 中溶褪。
autowíre 屬性有 5 個值币旧,其值及說明如下表所示。

屬性值 說明
default 由 <bean> 的上級標簽 <beans> 的 default-autowire 屬 性值確定 猿妈。 例如 <beans defaultautowire="byName"> 吹菱,則該<bean>元素中的 autowire 屬性對應(yīng)的屬性值就為 byName
byName 根據(jù)屬性的名稱自動裝配。 窯器將根據(jù)名稱查找與屬性完全一致的 Bean 彭则,并將真屬性自動裝配
byType 根據(jù)屬性的數(shù)據(jù)類型 (Type) 自動裝配毁葱,如果一個 Bean 的數(shù)據(jù)類型,兼容另一個 Bean 中屬性的 數(shù)據(jù)類型贰剥,則自動裝配
constructor 根據(jù)何造函數(shù)參數(shù)的數(shù)據(jù)類型倾剿,進行 byType 模式的自動裝配
no 在默認情況下,不使用自動裝配蚌成, Bean 依賴必須通過 ref 元素定義

下面通過修改上節(jié)中的案例來演示如何使用自動裝配前痘。
( 1 )修改上中的文件UserServicelmpl 和文件UserController,分別在文 件中增加類屬性的 setter 方法担忧。
(2 )修改上節(jié)中的配置文件 beans6.xml芹缔,將配置文件修改成自動裝配形式,文件如下所示瓶盛。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context" 
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-4.3.xsd 
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   <!-- 使用 bean 元素的 autowire 屬性完成自動裝配 -->
   <bean id="userDao" class="com.neuedu.annotation.UserDaolmpl"></bean>
   <bean id="userService" class="com.neuedu.annotation.UserServiceImpl" autowire="byName"></bean>
   <bean id="userController" class="com.neuedu.annotation.UserController" autowire="byName"></bean>
   
</beans>

上述配置文件中最欠,用于配置 userService 和 userControl1凹的<bean>元素中除了 id 和 class 屬性外,還增加了 autowire 屬性惩猫,并將其屬性值設(shè)置為 by隊lame芝硬。 在默認情況下,配置文件中 需要通過 ref 來裝配 Bean 轧房,但設(shè)置了 autowire=" byName"后拌阴, Spring 會自動尋找 userService Bean 中的屬性,并將其屬性名稱與配置文件中定義的 Bean 做匹配奶镶。 由于 UserServicelmpl 中 定義了 userDao 屬'性及其 setter 方法迟赃,這與配置文件中 id 為 userDao 的 Bean 相匹配,所以 Spring 會自動地將 id 為 userDao 的 Bean 裝配到 id 為 userService 的 Bean 中厂镇。
執(zhí)行程序后纤壁,控制臺的輸出結(jié)果如圖所示。



從圖中可以看出捺信,使用自動裝配同樣完成了依賴注入酌媒。

本章小結(jié)

本章主要對 Spring 中的 Bean 進行了詳細講解。 首先介紹了 Bean 的配置,然后通過案例講 解了 Bean 實例化的三種方式;接下來介紹了 Bean 的作用域和生命周期;最后講解了 Bean 的 三種裝配方式馍佑。 通過本章的學習斋否,讀者可以了解 Bean 的常用屬性及其作用梨水,可以掌握實例化 Bean 的三種方式拭荤,熟悉 Bean 作用域的種類及其生命周期,掌握 Bean 的三種裝配方式疫诽。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舅世,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奇徒,更是在濱河造成了極大的恐慌雏亚,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摩钙,死亡現(xiàn)場離奇詭異罢低,居然都是意外死亡,警方通過查閱死者的電腦和手機胖笛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門网持,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人长踊,你說我怎么就攤上這事功舀。” “怎么了身弊?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵辟汰,是天一觀的道長。 經(jīng)常有香客問我阱佛,道長帖汞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任凑术,我火速辦了婚禮涨冀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘麦萤。我一直安慰自己鹿鳖,他們只是感情好,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布壮莹。 她就那樣靜靜地躺著翅帜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪命满。 梳的紋絲不亂的頭發(fā)上涝滴,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音,去河邊找鬼歼疮。 笑死杂抽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的韩脏。 我是一名探鬼主播缩麸,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赡矢!你這毒婦竟也來了杭朱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吹散,失蹤者是張志新(化名)和其女友劉穎弧械,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體空民,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡刃唐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了界轩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片画饥。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖耸棒,靈堂內(nèi)的尸體忽然破棺而出荒澡,到底是詐尸還是另有隱情,我是刑警寧澤与殃,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布单山,位于F島的核電站,受9級特大地震影響幅疼,放射性物質(zhì)發(fā)生泄漏米奸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一爽篷、第九天 我趴在偏房一處隱蔽的房頂上張望悴晰。 院中可真熱鬧,春花似錦逐工、人聲如沸铡溪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棕硫。三九已至,卻和暖如春袒啼,著一層夾襖步出監(jiān)牢的瞬間哈扮,已是汗流浹背纬纪。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滑肉,地道東北人包各。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像靶庙,于是被迫代替她去往敵國和親问畅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容