天天看點

[spring]03_裝配Bean3.1 JavaBean3.2 聲明Bean3.3 注入Bean屬性3.4 使用表達式裝配 3.5 JavaBean的一個簡單應用執行個體

JavaBean 是一種JAVA語言寫成的可重用元件。

為寫成JavaBean,類必須是具體的和公共的,并且具有無參數的構造器。

JavaBean 通過提供符合一緻性設計模式的公共方法将内部域暴露成員屬性。

以下是一個簡單的JavaBean類。

定義一個Person類,有 name 和 age 兩個屬性,以及這兩個屬性的 get、set 方法。 

<a></a>

package com.demo.web.controllers;

public class Person {

    private String name = "zhangsan";

    private int age = 28;

    public Person() {

    }

    public Person(String name, int age) {

        this.name = name;

        this.age = age;

    public String getName() {

        return name;

    public void setName(String name) {

    public int getAge() {

        return age;

    public void setAge(int age) {

}

JavaBean 是一個 public 類型的類

JavaBean 含無參數的構造函數

JavaBean 提供 set 和 get 方法 

傳統JavaBean生命周期

傳統的Java應用,Bean的生命周期很簡單。

使用new進行執行個體化,然後該Bean就可以使用了。程式結束後,Java會自動進行垃圾回收。

Spring容器中的JavaBean生命周期

在Spring容器中的Bean的生命周期要複雜多了,步驟如下:

(1)Spring對Bean進行執行個體化。

(2)Spring将值和Bean的引用注入進Bean對應的屬性中。

(3)如果 Bean 實作了 BeanNameAware 接口,Spring将 Bean 的ID傳遞給 setBeanName() 接口方法。

(4)如果 Bean 實作了 BeanFactoryAware 接口,Spring将調用 setBeanFactory() 接口方法,将BeanFactory 容器執行個體傳入。

(5)如果 Bean 實作了 ApplicationContextAware 接口,Spring将調用 setApplicationContext() 接口方法,将應用上下文的引用傳入。

(6)如果 Bean 實作了 BeanPostProcessor 接口,Spring将調用它們的 post-ProcessBeforeInitialization接口方法。

(7)如果 Bean 實作了 InitializingBean 接口,Spring将調用它們的 afterPropertiesSet 接口方法。類似地,如果 Bean 使用 init-method 聲明了初始化方法,該方法也會被調用。

(8) 如果 Bean 實作了 BeanPostProcessor接口,Spring将調用它們的 post-ProcessAfterInitialization接口方法。

(9)此時此刻,Bean 已經準備就緒,可以被應用程式是用來,它們将一直駐留在應用上下文中,直到該應用上下文被銷毀。

(10)如果 Bean 實作了 DisposableBean 接口,Spring将調用它的 destroy() 接口方法。同樣,如果Bean使用 destroy-method 聲明了銷毀方法,該方法也會被調用。

建立應用對象之間協作關系的行為通常被稱為裝配,這也是依賴注入的本質。

Spring是一個基于容器的架構。但是如果沒有配置Spring,那它就是一個空容器,當然也毫無用處。

是以需要配置Spring,以告訴容器需要加載哪些Bean和如何裝配這些Bean。

從Spring3.0開始,Spring容器提供了兩種配置Bean的方式。

使用XML檔案作為配置檔案

基于Java注解的配置方式 

注:本文先不介紹注解的配置方式,而是重點介紹傳統的XML配置方式。

以下是一個典型的XML配置檔案(一般為&lt;servlet名&gt;-servlet.xml檔案)

&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;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-3.0.xsd"&gt;

    &lt;!-- Bean declarations go here --&gt;

&lt;/beans&gt;

在 &lt;beans&gt; 元素内,可以放置所有的Spring配置資訊。

實際上,beans這個标簽是 Spring的一種命名空間。

Spring架構自帶了10個命名空間,如下表所示:

命名空間

用途

aop

為聲明切面以及将@AspectJ注解的類代理為Spring切面提供了配置元素

beans

支援聲明Bean和裝配Bean,是Spring最核心也是最原始的命名空間

context

為配置Spring應用上下文提供了配置元素,包括自動檢測盒自動裝配Bean、注入非Spring直接管理的對象

jee

提供了與Java EE API的內建,例如JNDI和EJB

jms

為聲明消息驅動的POJO提供了配置元素

lang

支援配置由Groovy、Jruby或BeanShell等腳本是實作的Bean

mvc

啟用SpringMVC的能力,例如面向注解的控制器、試圖控制器和攔截器

oxm

支援Spring的對象到XML映射配置

tx

提供聲明式事務配置

util

提供各種各樣的工具類元素,包括把集合配置為Bean、支援屬性占位符元素

除了Spring架構自帶的命名空間,Spring Portfolio的許多成員,例如Spring security、Spring Web Flow和Spring Dynamic Modules,同樣提供了他們自己的命名空間配置。

以上文提到的Person 類為例,定義了JavaBean後,還需要在xml檔案中聲明,形式如下:

&lt;bean id="person" class="com.demo.web.controllers.Person"/&gt;

這個标簽的意思就是,建立一個 com.demo.web.controllers.Person 類的對象person。

當Spring容器加載這個Bean的時候,會使用預設構造器來執行個體化person對象,相當于:

com.demo.web.controllers.Person person = new com.demo.web.controllers.Person();

備注實際上,Spring是使用反射機制來建立Bean的。

很多應用場景下,我們希望在初始化執行個體時,傳入關鍵性的參數,這就需要帶參數的構造函數了。

為了解決這個問題,在構造Bean的時候,可以使用 &lt;constructor-arg&gt; 元素來指定構造器參數。

&lt;bean id="person" class="com.demo.web.controllers.Person"&gt;

    &lt;constructor-arg value="lisi" /&gt;

    &lt;constructor-arg value="28" /&gt;

&lt;/bean&gt;

如果不使用這個标簽,spring将使用預設構造函數。

以上參數都是直接傳入值,如果想傳入對象引用,那要怎麼做呢?

請參考下面的例子:

首先定義一個Car類

public class Car {

    private String model;

    public Car() {

    public Car(String model) {

        this.model = model;

    public void run() {

        System.out.println(model + " 正在行駛");

再定義一個Driver類

public class Driver {

    private Car car;

    public Driver() {

    public Driver(Car car) {

        this.car = car;

    public void drive() {

        car.run();

接下來,我們可以在XML中配置一個Car的變量

&lt;bean id="car" class="com.demo.web.controllers.Car"&gt;

    &lt;constructor-arg value="寶馬5系" /&gt;

有了Car的變量car,我們就可以在聲明Driver變量時,使用 &lt;constructor-arg&gt; 标簽引用它。

&lt;bean id="driver" class="com.demo.web.controllers.Driver"&gt;

    &lt;constructor-arg ref="car" /&gt;

有時候一個類并沒有public型的構造方法(典型的如單例模式裡的類),對于這種情況如何在spring中執行個體化呢?

這時候靜态工廠方法是執行個體化對象的唯一方法。Spring支援通過 &lt;bean&gt; 元素的 factory-method 屬性來裝配工廠建立的Bean。

public class Singleton {

    static Singleton instance = new Singleton();

    private Singleton() {

    public static Singleton getInstance() {

        return instance;

為了在Spring中将Singleton配置為Bean,可以按照下面的方式來配置

&lt;bean id="instance" class="com.demo.web.controllers.Singleton"

        factory-method="getInstance" /&gt;

所有的Spring Bean預設都是單例。當容器配置設定一個Bean時,它總是傳回Bean的同一個執行個體。

但有時我們需要每次請求時都獲得唯一的Bean執行個體,如何做到呢?

當在Spring中配置 &lt;bean&gt; 元素時,我們可以為Bean聲明一個作用域。為了讓Spring在每次請求時都為Bean産生一個新的執行個體,我們隻需要配置Bean的scope屬性為 prototype即可。

&lt;bean id="person" class="com.demo.web.controllers.Person" scope="prototype" /&gt;

除了prototype,Spring還提供了其他幾個作用域選項,如下:

作用域

定義

singleton

在每一個Spring容器中,一個Bean定義隻有一個對象執行個體(預設)

prototype

運作Bean的定義可以被執行個體化任意次(每次調用都建立一個執行個體)

request

在一次HTTP請求中,每個Bean定義對應一個執行個體,該作用域僅在基于Web的Spring上下文(例如Spring MVC)中才有效

session

在一個HTTP Session中,每個Bean定義對應一個執行個體,該作用域僅在基于Web的Spring上下文(例如Spring MVC)中才有效

global-session

在一個全局HTTP Session中,每個Bean定義對應一個執行個體,該作用域僅在Portlet上下文中才有效

注:Spring的單例Bean隻能保證在每個應用上下文中隻有一個Bean的實 例。 

當執行個體化一個Bean時,可能需要執行一些初始化操作來確定該Bean處于可用狀态。同樣地,當不再需要Bean,将其從容器中移除時,我們可能還需要按 順序執行一些清除工作。為了滿足初始化和銷毀Bean的需求,Spring提供了Bean生命周期的鈎子方法。

為Bean定義初始化和銷毀操作,隻需要使用 init-method 和 destroy-method 參數來配置 &lt;bean&gt; 元素。 init-method 屬性指定了在初始化 Bean 時要調用的方法。類似地,destroy-method 屬性指定了 Bean 從容器移除之前要調用的方法。 

假設,為一個燈泡的功能寫一個類。

public class Light {

    public void turnOn() {

        // ...

    public void turnOff() {

接着,我們在XML中做如下配置

&lt;bean id="light" class="com.demo.web.controllers.Light"

        init-method="turnOn" destroy-method="turnOff" /&gt;

這樣,就能保證讓燈泡類在點亮之前調用turnOn(),結束時調用turnOff()。

通常,JavaBean中的屬性都是私有的,同時提供一組get、set方法。 

(1)注入簡單值

在Spring中,除了用前面介紹的構造器注入方式,還可以使用 &lt;property&gt; 元素配置 Bean 的屬性。 

還是以Person類為例

    &lt;property name="name" value="wangwu" /&gt;

    &lt;property name="age" value="30" /&gt;

(2)引用其他Bean

還記得我們在構造器注入部分提到的Driver和Car類的例子嗎,如果使用property的方式,則按如下方式表達

&lt;bean id="baoma" class="com.demo.web.controllers.Car"&gt;

    &lt;property name="car" ref="baoma" /&gt;

(3)内部注入

内部注入是通過直接聲明一個 &lt;bean&gt; 元素作為 &lt;property&gt;元素的子節點而定義的。

    &lt;property name="car"&gt;

        &lt;bean class="com.demo.web.controllers.Car" /&gt;

    &lt;/property&gt;

(4)使用Sping的命名空間p裝配屬性

Spring還提供了一個命名空間p以簡化&lt;property&gt;元素的裝配方式。 

命名空間p的schema URI為 http://www.springframework.org/schema/p。

如果要使用命名空間p ,需要在Spring的XML配置中增加一段聲明

    xmlns:p="http://www.springframework.org/schema/p" 

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

有了以上聲明,我們就可以使用p:作為&lt;bean&gt;元素所有屬性的字首來裝配Bean的屬性。

用p:重新定義上節Driver類的配置, 如下:

&lt;bean id="driver" class="com.demo.web.controllers.Driver"

    p:car-ref="baoma"/&gt;

p:car-ref="baoma" 表示要裝配car 屬性,-ref字尾表示這是一個引用而不是字面值。"baoma"是這個屬性的引用對象。

Spring不僅可以裝配單個值,也可以裝配集合。

Spring提供4種類型的集合配置元素。

集合元素

對應實際資料類型

&lt;list&gt;

裝配list類型的值,允許重複

數組或java.util.Collection

&lt;set&gt;

裝配set類型的值,不允許重複

&lt;map&gt;

裝配map類型的值,名稱和值可以是任意類型

java.util.Map

&lt;props&gt;

裝配properties類型的值,名稱和值必須都是String型

java.util.Properties

(1)&lt;list&gt; 和 &lt;set&gt;

&lt;list&gt; 和 &lt;set&gt; 都可以用來裝配類型為 java.util.Collection 的任意實作或數組的屬性。

兩者的差別在于:list 允許成員重複; set 必須保證成員不重複。

需要注意的是,不是說如果屬性是java.util.Set 類型,則使用者必須用 &lt;set&gt; 裝配。

定義一個 instruments 屬性,它是一個Collection,儲存各種樂器 

&lt;property name="instruments"&gt;

    &lt;list&gt;

        &lt;ref bean="guitar" /&gt;

        &lt;ref bean="piano" /&gt;

    &lt;/list&gt;

&lt;/property&gt;

    &lt;set&gt;

        &lt;ref bean="piano" /&gt;&lt;!-- 自動忽略重複的屬性 --&gt;

    &lt;/set&gt;

(2)&lt;map&gt;

&lt;map&gt; 元素聲明了一個 java.util.Map 類型的值。每個 &lt;entry&gt; 元素定義 Map 的一個成員。

&lt;entry&gt; 元素由一個鍵和一個值組成,鍵和值可以是簡單類型,也可以是其他Bean的引用。

屬性

key

指定map中entry的鍵為String

key-ref

指定map中entry的鍵為Spring上下文中其他Bean的引用

value

指定map中entry的值為String

value-ref

指定map中entry的值為Spring上下文中其他Bean的引用

    &lt;map&gt;

        &lt;entry key="GUITAR" value-ref="guitar"&gt;

        &lt;entry key="PIANO" value-ref="piano"&gt;

    &lt;/map&gt;

(3)&lt;prop&gt;

如 果 Map 的每一個 entry 的鍵和值都為 String 類型時,可以考慮使用 java.util.Properties 代替 Map。Properties 類提供了和 Map 大緻相同的功能,但是它限定了鍵和值必須為 String 類型。

&lt;props&gt; 元素建構了一個 java.util.Properties 值,這個 Properties 的每一個成員由 &lt;prop&gt; 定義。

    &lt;props&gt;

        &lt;prop key="GUITAR"&gt;guitar sound&lt;/prop&gt;

        &lt;prop key="PIANO"&gt;piano sound&lt;/prop&gt;

    &lt;/props&gt;

Spring 3引入了Spring表達式語言(Spring Expression Lanuage, SpEL)。

它通過運作期執行的表達式将值裝配到 Bean 的屬性或構造器參數中。 

SpEL 表達式的首要目标是通過計算獲得某個值。最簡單的SpEL求值或許是對字面值、Bean的屬性或某個類的常量進行求值。 

字面值

&lt;property&gt; 元素的 value 屬性中使用 #{} 界定符把這個值裝配到 Bean 的屬性中

&lt;property name="count" value="#{5}" /&gt;

此外,也可以與非SpEL 表達式的值混用

&lt;property name="count" value="The value is #{5}" /&gt;

引用Bean、Properties 和方法

SpEL 表達式可以通過 ID 引用其他 Bean。

&lt;property name="fruit" value="#{apple}" /&gt;

這和以下語句的功能等價

&lt;property name="fruit" ref="apple" /&gt;

除了直接引用其他Bean,也可以引用 Bean 對象的屬性和方法

&lt;property name="song" value="#{singer.song}" /&gt;

&lt;property name="song" value="#{singer.selectSong().toUpperCase()}" /&gt;

需要注意的是,singer.selectSong().toUpperCase()存在一個問題,如果selectSong()方法傳回的是null, 那麼SpEL表達式求值時會抛出一個 NullPointerException 異常。

為了避免這種情況,可以使用null-safe存取器(?.)

&lt;property name="song" value="#{singer.selectSong()?.toUpperCase()}" /&gt;

使用 ?. 代替 . 來通路 toUpperCase() 方法。?. 可以確定隻有當 selectSong() 不為 null 時才去調用 toUpperCase() 方法。

操作類

現在,我們了解了在 SpE L中,如何去調用 Bean 對象,以及對象的屬性和方法。

但是,如何去通路類的靜态方法或常量引用呢?

在 SpEL 中,使用 T() 運算符去調用類作用域的方法和常量。

以下示範了如何調用 java.lang.Math 類中的靜态方法和屬性。 

&lt;property name="multiplier" value="#{T{java.lang.Math}.PI}" /&gt;

&lt;property name="multiplier" value="#{T{java.lang.Math}.random}" /&gt;

在 SpEL 值上執行運算操作

SpEL提供了幾種運算符,這些運算符可以用在SpEL表達式中的值上。

運算符類型

運算符

算術運算

+、-、*、/、%、^

關系運算

&lt;、&gt;、==、&lt;=、&gt;=、lt、gt、eq、le、ge

邏輯運算

and、or、not、|

條件運算

?: (ternary)、?: (Elvis)

正規表達式

matches

假設針對前面的Person類,我們定義一個List集合,如下: 

&lt;util:list id="persons"&gt;

    &lt;bean class="com.demo.web.controllers.Person" p:name="zhangsan" p:age="17"/&gt;

    &lt;bean class="com.demo.web.controllers.Person" p:name="lisi" p:age="24"/&gt;

    &lt;bean class="com.demo.web.controllers.Person" p:name="wangwu" p:age="30"/&gt;

    &lt;bean class="com.demo.web.controllers.Person" p:name="zhaoliu" p:age="16"/&gt;

    &lt;bean class="com.demo.web.controllers.Person" p:name="liuqi" p:age="23"/&gt;

&lt;/util:list&gt;

通路集合成員

可以使用 [] 運算符來通路集合成員

&lt;property name="choosePerson" value="#{persons[T{java.lang.Math}.random() * persons.size()]]}" /&gt;

以上表示,随機選取一個人。

也可以按下面方式選取

&lt;property name="choosePerson" value="#{persons['zhangsan']}" /&gt;&lt;!-- 選取集合中叫張三的人 --&gt;

&lt;property name="choosePerson" value="#{persons[2]}" /&gt;&lt;!-- 選取集合中第二個人 --&gt;

查詢集合成員

如果想要在persons集合中查詢年齡大于18歲的人。 

在SpEL中,隻需使用一個查詢運算符(.?[]) 就可以簡單做到,如下所示:

&lt;property name="adults" value="#{persons.?[age gt 18]}" /&gt;

查詢運算符會建立一個新的集合,集合中隻存放符合括号中表達式的成員。 

SpEL 還提供兩種運算符:.^[] 和 .$[],從集合中查詢出第一個比對項和最後一個比對項。 

投影集合

在SpEL中, 提供了投影運算符(.![])将集合中每個成員的特定屬性放入一個新的集合中。

&lt;property name="personNames" value="#{persons.![name]}" /&gt;

以上,将所有人的名字取出來,建立一個新的集合personNames。

打開 HelloWorld 工程。

(1)建立一個 java 檔案,名為 Person.java,完整内容如下:

 View Code

(2)修改 index.jsp 檔案,完整内容如下:

(3)運作

結果如下:

本文轉自靜默虛空部落格園部落格,原文連結:http://www.cnblogs.com/jingmoxukong/p/4532680.html,如需轉載請自行聯系原作者