1. 程式人生 > >Spring系列之bean的使用

Spring系列之bean的使用

ack 工廠 使用實例 自動裝配 繼承 放心 sys xmla 客戶端

一、Bean的定義

<bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/>
這是一個最簡單的 Bean 定義。它類似於調用了語句:
MemoryUserDao userDao = new MemoryUserDao()。

id屬性必須是一個有效的 XML ID,這意味著它在整個 XML 文檔中必須唯一。它是一個 Bean 的“終身代號”。同時你也可以用 name 屬性為 Bean 定義一個或多個別名(用逗號或空格分開多個別名)。name 屬性允許出現任意非法的 XML 字母。例如:

<bean id="userDao" name="userDao*_1, userDao*_2" class="com.dev.spring.simple.MemoryUserDao"/>。

class屬性定義了這個 Bean 的全限定類名(包名+類名)。Spring 能管理幾乎所有的 Java 類。一般情況,這個 Java 類會有一個默認的構造函數,用set方法設置依賴的屬性。

Bean 元素出了上面的兩個屬性之外,還有很多其它屬性。說明如下:

技術分享
<bean
    id="beanId"(1)
    name="beanName"(2)
    class="beanClass"(3)
    parent="parentBean"(4)
    abstract="true | false"(5)
    singleton="true | false"(6)
    lazy-init="true | false | default"(7)
    autowire="no | byName | byType | constructor | autodetect | default"(8)
    dependency-check = "none | objects | simple | all | default"(9)
    depends-on="dependsOnBean"(10)
    init-method="method"(11)
    destroy-method="method"(12)
    factory-method="method"(13)
    factory-bean="bean">(14)
</bean>
技術分享

(1)id: Bean 的唯一標識名。它必須是合法的 XML ID,在整個 XML 文檔中唯一。

(2)name: 用來為 id 創建一個或多個別名。它可以是任意的字母符合。多個別名之間用逗號或空格分開。

(3)class: 用來定義類的全限定名(包名+類名)。只有子類 Bean 不用定義該屬性。

(4)parent: 子類 Bean 定義它所引用它的父類 Bean。這時前面的 class 屬性失效。子類 Bean 會繼承父類 Bean 的所有屬性,子類 Bean 也可以覆蓋父類 Bean 的屬性。註意:子類 Bean 和父類 Bean 是同一個 Java 類。

(5)abstract(默認為”false”):用來定義 Bean 是否為抽象 Bean。它表示這個 Bean 將不會被實例化,一般用於父類 Bean,因為父類 Bean 主要是供子類 Bean 繼承使用。

(6)singleton(默認為“true”):定義 Bean 是否是 Singleton(單例)。如果設為“true”,則在 BeanFactory 作用範圍內,只維護此 Bean 的一個實例。如果設為“flase”,Bean將是 Prototype(原型)狀態,BeanFactory 將為每次 Bean 請求創建一個新的 Bean 實例。

(7)lazy-init(默認為“default”):用來定義這個 Bean 是否實現懶初始化。如果為“true”,它將在 BeanFactory 啟動時初始化所有的 Singleton Bean。反之,如果為“false”,它只在 Bean 請求時才開始創建 Singleton Bean。

(8)autowire(自動裝配,默認為"default"):它定義了 Bean 的自動裝載方式。
"no":不使用自動裝配功能。
"byName":通過 Bean 的屬性名實現自動裝配。
"byType":通過 Bean 的類型實現自動裝配。
"constructor":類似於 byType,但它是用於構造函數的參數的自動組裝。
"autodetect":通過 Bean 類的反省機制(introspection)決定是使用“constructor”還是使用“byType”。

(9)dependency-check(依賴檢查,默認為“default”):它用來確保 Bean 組件通過 JavaBean 描述的所以依賴關系都得到滿足。在與自動裝配功能一起使用時,它特別有用。
none:不進行依賴檢查。
objects:只做對象間依賴的檢查。
simple:只做原始類型和 String 類型依賴的檢查
all:對所有類型的依賴進行檢查。它包括了前面的 objects 和 simple。

(10)depends-on(依賴對象):這個 Bean 在初始化時依賴的對象,這個對象會在這個 Bean 初始化之前創建。

(11)init-method:用來定義 Bean 的初始化方法,它會在 Bean 組裝之後調用。它必須是一個無參數的方法。

(12)destroy-method:用來定義 Bean 的銷毀方法,它在 BeanFactory 關閉時調用。同樣,它也必須是一個無參數的方法。它只能應用於singleton Bean。

(13)factory-method:定義創建該 Bean 對象的工廠方法。它用於下面的"factory-bean",表示這個 Bean 是通過工廠方法創建。此時,"class"屬性失效。

(14)factory-bean:定義創建該 Bean 對象的工廠類。如果使用了"factory-bean"則"class"屬性失效。

二、實例化Bean的三種方式

1.使用構造器實例化Bean

這是最簡單的方式,Spring IOC容器即能使用默認空構造器也能使用有參數構造器兩種方式創建Bean。
使用空構造器進行定義,class屬性指定的類必須有空構造器。使用有參數構造器進行定義,可以使用< constructor-arg >標簽指定構造器參數值,其中index表示位置,value表示常量值,也可以指定引用,指定引用使用ref來引用另一個Bean定義,後邊會詳細介紹。下面來看一個例子:

(1)定義一個接口

package com.spring.service;

public interface IUserService {
    public void show();
}

(2)實現類,該類有一個空構造器和一個有參構造器:

技術分享
package com.spring.service.impl;

import com.spring.service.IUserService;

public class UserServiceImpl implements IUserService{
    
    private String message;
    
    public UserServiceImpl(){
        this.message="lixiaoxi";
    }
    
    public UserServiceImpl(String message){
        this.message=message;
    }
    
    public void show(){
        System.out.println("hello,"+message);
    }
}
技術分享

(3)在配置文件(applicationContext1.xml)中配置Bean定義,如下所示:

技術分享
<!-- 使用默認構造函數 -->
<bean id="bean1" class="com.spring.service.impl.UserServiceImpl">  
</bean> <!-- 使用有參數構造函數 --> <bean id="bean2" class="com.spring.service.impl.UserServiceImpl" > <!-- 指定構造器參數 --> <constructor-arg index="0" value="zhangsan" /> </bean>
技術分享

(4)測試方法:

技術分享
/**
 * 使用構造器實例化Bean
 */
@Test
public void testCreateBeanByConstructor(){
    //讀取配置文件
    ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext1.xml");
    //獲取bean的實例
    IUserService bean1=(IUserService) ctx.getBean("bean1");
    //調用方法
    bean1.show();
    
    IUserService bean2=(IUserService) ctx.getBean("bean2");
    bean2.show();
}
技術分享

2.使用靜態工廠方法實例化Bean

使用這種方式除了指定必須的class屬性,還要指定factory-method屬性來指定實例化Bean的方法,而且使用靜態工廠方法也允許指定方法參數,spring IoC容器將調用此屬性指定的方法來獲取Bean。

(1)定義靜態工廠類:

技術分享
package com.spring.factory;

import com.spring.service.IUserService;
import com.spring.service.impl.UserServiceImpl;

public class UserStaticFactory {
    //工廠方法
    public static IUserService newInstance(String message){
        //返回需要的Bean實例
        return new UserServiceImpl(message);
    }
}
技術分享

(2)在配置文件(applicationContext1.xml)中配置Bean定義,如下所示:

  <!-- 使用靜態工廠方法 -->
  <bean id="bean3" class="com.spring.factory.UserStaticFactory" factory-method="newInstance" >
      <constructor-arg index="0" value="lisi" />
  </bean>

(3)測試方法:

技術分享
/**
 * 使用靜態工廠實例化Bean
 */
@Test
public void testCreateBeanByStaticFactory(){
    ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext1.xml");
    IUserService bean3=(IUserService) ctx.getBean("bean3");
    bean3.show();
}
技術分享

3.使用實例工廠方法實例化Bean

使用這種方式不能指定class屬性,此時必須使用factory-bean屬性來指定工廠Bean,factory-method屬性指定實例化Bean的方法,而且使用實例工廠方法允許指定方法參數,方式和使用構造器方式一樣,配置如下:

(1)定義實例工廠類:

技術分享
package com.spring.factory;

import com.spring.service.IUserService;
import com.spring.service.impl.UserServiceImpl;

public class UserInstanceFactory {
    
    public IUserService newInstance(String message){
        return new UserServiceImpl(message);
    }
}
技術分享

(2)在配置文件(applicationContext1.xml)中配置Bean定義,如下所示:

  <!-- 1.定義實例工廠Bean -->
  <bean id="beanInstanceFactory" class="com.spring.factory.UserInstanceFactory" />
  <!-- 2.使用實例工廠Bean創建Bean -->
  <bean id="bean4" factory-bean="beanInstanceFactory" factory-method="newInstance" >
       <constructor-arg index="0" value="aaaa"></constructor-arg>
  </bean> 

(3)測試方法:

技術分享
/**
 * 使用實例工廠實例化Bean
 */
@Test
public void testCreateBeanByInstanceFactory(){
    ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext1.xml");
    IUserService bean4=(IUserService) ctx.getBean("bean4");
    bean4.show();
}
技術分享

總結:

這三種方式只是配置不一樣,從獲取方式看完全一樣,沒有任何不同。這也是Spring IoC的魅力,Spring IoC幫你創建Bean,我們只管使用就可以了,是不是很簡單。

三、Bean的作用域

什麽是作用域呢?即“scope”,在面向對象程序設計中一般指對象或變量之間的可見範圍。而在Spring容器中是指其創建的Bean對象相對於其他Bean對象的請求可見範圍。
Spring提供“singleton”和“prototype”兩種基本作用域,另外提供“request”、“session”、“globalsession”三種web作用域;Spring還允許用戶定制自己的作用域。

作用域描述
singleton

在每個Spring IoC容器中一個bean定義對應一個對象實例。

(默認)在spring IOC容器中僅存在一個Bean實例,Bean以單實例的方式存在。

prototype

一個bean定義對應多個對象實例。

每次從容器中調用Bean時,都返回一個新的實例,即每次調用getBean()時,相當於執行new XxxBean()的操作。

request

在一次HTTP請求中,一個bean定義對應一個實例;即每次HTTP請求將會有各自的bean實例,它們依據某個bean定義創建而成。該作用域僅在基於web的Spring ApplicationContext情形下有效。

session

在一個HTTP Session中,一個bean定義對應一個實例。該作用域僅在基於web的Spring ApplicationContext情形下有效。

同一個HTTP session共享一個Bean,不同HTTP session使用不同的Bean,該作用域僅適用於webApplicationContext環境。

globalSession

在一個全局的HTTP Session中,一個bean定義對應一個實例。典型情況下,僅在使用portlet context的時候有效。該作用域僅在基於web的Spring ApplicationContext情形下有效。

1.singleton
“singleton”作用域的Bean只會在每個Spring IoC容器中存在一個實例,而且其完整生命周期完全由Spring容器管理。對於所有獲取該Bean的操作Spring容器將只返回同一個Bean。註意:spring將Bean的默認作用域定為singleton。

當一個bean的作用域設置為singleton, 那麽Spring IOC容器中只會存在一個共享的bean實例,並且所有對bean的請求,只要id與該bean定義相匹配,則只會返回bean的同一實例。換言之,當把 一個bean定義設置為singleton作用域時,Spring IOC容器只會創建該bean定義的唯一實例。這個單一實例會被存儲到單例緩存(singleton cache)中,並且所有針對該bean的後續請求和引用都將返回被緩存的對象實例,這裏要註意的是singleton作用域和GOF設計模式中的單例是完全不同的,單例設計模式表示一個ClassLoader中只有一個class存在,而這裏的singleton則表示一個容器對應一個bean,也就是說當一個bean被標識為singleton時候,spring的IOC容器中只會存在一個該bean。

在默認情況下,spring的ApplicationContext容器在啟動時,自動實例化所有singleton的Bean並緩存於容器中。
雖然啟動時會花費一些時間,但帶來兩個好處:
(1)首先對Bean提前的實例化操作會及早發現一些潛在的配置問題。
(2)其次Bean以緩存的方式保存,當運行時使用到該Bean時就無須再實例化了,加快了運行效率。
如果用戶不希望在容器啟動時提前實例化singleton的Bean,可以通過lazy-init屬性進行控制。但是lazy-init="true"的Bean在某些情況下依舊會提前實例化:如果該Bean被其它需要提前實例化的Bean引用到,spring也將忽略延遲實例化的設置。

2.prototype

即原型,指每次向Spring容器請求獲取Bean都返回一個全新的Bean,相對於"singleton"來說就是不緩存Bean,每次都是一個根據Bean定義創建的全新Bean。

當使用prorotype作為作用域時,Bean會導致每次對該Bean的請求都創建一個Bean實例,所以對有狀態的Bean應該使用prorotype作用域,無狀態Bean 則使用singleton作用域。還有就是Spring不能對一個prototype作用域 bean的整個生命周期負責,容器在初始化、配置、裝飾或者是裝配完一個prototype實例後,將它交給客戶端,隨後就對該prototype實例不聞不問了。

在默認情況下,spring容器在啟動時不實例化prototype的Bean。此外,spring容器將prototype的Bean交給調用者後,就不再管理它的生命周期。

下面測試一下singleton與prototype,Java類用之前建的HelloWorld.java。

技術分享
package com.spring.test;

public class HelloWorld {
    private String info;

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
    
    public HelloWorld(){
        System.out.println("執行構造函數!");
    }
}
技術分享

配置文件applicationContext.xml:

<bean id="hello" class="com.spring.test.HelloWorld">  
    <property name="info" value="Hello,This is my first Spring Application!"></property>  
</bean> 

其中id為"hello"的bean聲明為singleton(因為默認是singleton,所以可以不顯示指定)
測試方法如下:

技術分享
/**
 * 測試Bean的作用域
 */
@Test
public void test(){
    //讀取配置文件
    ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
    //獲取bean的實例
    HelloWorld t=(HelloWorld) ctx.getBean("hello");
    HelloWorld t1=(HelloWorld) ctx.getBean("hello");
    System.out.println(t==t1);
}
技術分享

執行結果為:

技術分享

可以看到只打印了一次“執行構造函數!”,並且t=t1,說明它們是同一對象。

修改配置文件,將id為"hello"的bean的scope屬性改為"prototype"。

<bean id="hello" class="com.spring.test.HelloWorld" scope="prototype">  
    <property name="info" value="Hello,This is my first Spring Application!"></property>  
</bean> 

再次執行上面的測試方法,結果如下:

技術分享

打印了兩次“執行構造函數!”,並且t!=t1。

3.web應用中的作用域

在Web應用中,我們可能需要將數據存儲到request、session、global session。因此Spring提供了三種Web作用域:request、session、globalSession。

(1)request作用域request表示針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效。示例:

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

針對每次HTTP請求,Spring容器會根據loginAction bean定義創建一個全新的LoginAction bean實例, 且該loginAction bean實例僅在當前HTTP request內有效,因此可以根據需要放心的更改所建實例的內部狀態, 而其他請求中根據loginAction bean定義創建的實例,將不會看到這些特定於某個請求的狀態變化。 當處理請求結束,request作用域的bean實例將被銷毀。

(2)session作用域:針對每個會話,spring容器都會創建一個全新的Bean,且該Bean僅在當前HTTP Session內有效。

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

針對某個HTTP Session,Spring容器會根據userPreferences bean定義創建一個全新的userPreferences bean實例, 且該userPreferences bean僅在當前HTTP Session內有效。 與request作用域一樣,你可以根據需要放心的更改所創建實例的內部狀態,而別的HTTP Session中根據userPreferences創建的實例,將不會看到這些特定於某個HTTP Session的狀態變化。 當HTTP Session最終被廢棄的時候,在該HTTP Session作用域內的bean也會被廢棄掉。

(3)globalSession作用域:類似於session作用域,只是其用於portlet環境的web應用。如果在非portlet環境將視為session作用域。

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

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

請註意,假如你在編寫一個標準的基於Servlet的web應用,並且定義了一個或多個具有global session作用域的bean,系統會使用標準的HTTP Session作用域,並且不會引起任何錯誤。

Spring系列之bean的使用