Spring框架參考手冊_5.0.0_中文版_Part II_3.4
3.4 依賴
標準企業應用不會由一個物件(或Spring用語中的bean)組成。即使是最簡單的應用也是由一些物件共同工作,呈現給終端使用者使用者看到的是一個連貫的應用。接下來的一節闡述瞭如何從定義許多獨立的bean定義到完全實現的應用,它是一個通過物件協作來實現目標的過程。
3.4.1 依賴注入
依賴注入(DI)是一個處理過程,憑藉物件之間依賴關係,也就是和它們一起工作的其它物件,只能通過建構函式引數,傳遞引數給工廠方法,在構造完成或工廠方法返回的物件例項之後再設定物件例項的屬性。當建立bean時容器再將這些依賴物件注入進去。這個過程從根本上顛倒了bean本身通過直接構建類或通過一種機制例如服務定位模式來控制依賴物件的例項化或定位,因此命名為控制反轉(IoC)
使用依賴注入原則會使程式碼更簡潔,當物件由依賴關係提供時解耦更有效。物件不會查詢它的依賴,不知道依賴的位置和依賴關係的類別。同樣的,你的類也變的更容易測試,尤其是依賴關係在介面或抽象基類之間的時候,這種情況下單元測試中會要求存樁或模擬實現。(注:Stub和Mock都是軟體測試中使用的東西,如有疑問請自行google或百度)。
依賴有兩個主要變種,基於建構函式的依賴注入和基於Setter的依賴注入。
基於建構函式的依賴注入
基於建構函式的依賴注入通過容器呼叫有引數的建構函式來實現,每個引數表示一個依賴。呼叫指定引數的靜態工廠方法來構造bean是近似等價的,這裡的討論將給建構函式和靜態工廠方法傳參看成是類似的。接下來的例子展示了一個類僅能通過構建函式注入進行依賴注入。注意這個類沒什麼特別的,它是一個POJO,不依賴於容器特定的介面,基類或註解。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
建構函式引數解析
建構函式引數解析使用引數型別進行匹配。如果bean定義的建構函式引數中不存在潛在的歧義,bean定義中定義建構函式引數的順序為bean例項化時提供給恰當建構函式的引數順序。細想下面的類:
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
不存在潛在的歧義,假設Bar
類和Baz
類之間不存在繼承關係。因此下面的配置會工作良好,你不必在<constructor-arg/>
元素中顯式的指定建構函式引數索引的與/或型別。
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>
當引用另一個bean時,型別已知,匹配正確(像上面的例子一樣)。當使用簡單型別時,例如<value>true</value>
,Spring不能決定值的型別,因此沒有幫助不能按型別匹配。考慮下面的例子:
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
在上面的場景中,如果你用type
屬性顯式的指定了構造引數的型別,對於簡單型別容器可以使用型別匹配。例如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
使用index
屬性來顯式的指定建構函式引數的索引,例如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
除了要解析多個簡單值的歧義性之外,當建構函式有兩個相同型別的的引數時,指定索引可以解決歧義性問題。注意索引是從0開始的。
你也可以使用建構函式引數名字解決值的歧義問題。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
記住,要使這個起作用你的程式碼必須使用除錯模式進行編譯,這樣Spring可以從建構函式中查詢引數名稱。如果你不能用除錯模式進行編譯(或不想),你可以使用JDK註解@ConstructorProperties
顯式的命名你的建構函式引數。樣板類如下所示:
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
基於Setter的依賴注入
基於Setter的依賴注入在容器呼叫無參建構函式或無參靜態工廠方法之後,通過呼叫bean的setter方法來實現依賴注入。
下面的例子顯示了一個類只能通過純粹的setter注入進行依賴注入。這個類是常見的Java類。它是一個不依賴於容器中特定介面、基類或註解的POJO。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
ApplicationContext
支援基於建構函式和基於setter對它管理的bean進行依賴注入。它也支援一些依賴通過建構函式方法注入之後,使用基於setter的依賴注入。使用BeanDefinition
形式配置依賴項,結合PropertyEditor
例項可以將屬性從一種形式轉成另一種形式。然而大多數Spring使用者直接使用這些類(例如以程式設計形式),而使用XML定義bean,註解元件(例如類中使用 @Component
,,@Controller
註解等等),或在基於Java的@Configuration
類使用@Bean
方法。
使用基於建構函式的依賴注入還是基於setter的依賴注入?
你可以混合使用基於建構函式的依賴注入和基於setter的依賴注入,強制依賴使用建構函式注入,可選依賴使用setter方法或配置方法注入是一個很好的經驗法則。注意在setter方法上使用
@Required
註解會檢查依賴是否注入。當實現的應用元件是不可變物件時,Spring團隊通常主張建構函式注入,這樣可以確保所需的依賴非空。此外,基於建構函式注入的元件總是以完全初始化狀態返回客戶(呼叫)程式碼。作為附註,含有許多建構函式引數的程式碼給人的感覺很差,這意味著類可能有很多職責,應該進行重構以便更好的處理關注問題的分離。
setter注入應該主要用來可選依賴上,在類內可以給可選依賴指定合理的預設值。此外,在每處使用依賴的程式碼都要進行非空檢查。setter注入的一個好處就是setter方法使類的物件在後面可以進行再配置或再注入。
JMX MBeans
的管理是setter注入一個非常好的案例。使用依賴注入的型別對於特定的類是最有意義的。有時候,當處理沒有原始碼的第三方類時,使用哪種方式取決於你。例如,如果第三方庫沒有提供任何setter方法,建構函式注入可能是依賴注入唯一可行的方式。
依賴解析過程
容器按下面的過程處理bean依賴解析:
建立
ApplicationContext
並使用描述所有bean的配置元資料初始化ApplicationContext
,配置元資料可以通過XML,Java程式碼或註解指定。對於每一個bean,它的依賴通過屬性、建構函式引數、或靜態工廠方法引數的形式表示,靜態工廠方法可以替代標準的建構函式。當bean在實際建立時,這些依賴會提供給bean。
每個屬性或建構函式引數或者是根據實際定義設定的值,或者是容器中另一個bean的引用。
每個屬性或建構函式引數是一個從指定形式轉成實際型別的屬性或建構函式引數的值。
當容器建立後Spring容器會驗證每個bean的配置。然而,bean屬性本身只有bean建立時才會進行設定。bean是單例的並且當容器建立時會進行提前例項化(預設情況)。作用範圍是在3.5 小節”Bean scopes”中定義的。此外,只有需要時候才會建立bean。bean的建立可能會引起beans圖的建立,當bean的依賴和它的依賴的依賴(等等)建立和指定的時候。注意這些依賴中解析不匹配可能會在後面出現,例如,受影響的bean第一次建立時。
迴圈依賴
如果你主要使用建構函式注入,有可能會出現一個不能解決的迴圈依賴狀況。
例如,類A需要通過建構函式注入得到一個類B的例項,而類B需要通過建構函式獲得一個類A的例項。如果你為類A和類B配置了互相注入的bean,Spring IoC容器在執行時檢測到迴圈引用,會丟擲
BeanCurrentlyInCreationException
。一個可能的解決方案是編譯某個類的原始碼使其通過setter注入而不是建構函式注入。或者,避免建構函式注入僅用setter注入。換句話說,儘管是不被推薦的,但你可以通過setter注入配置迴圈依賴。
不像通常的情況(沒有迴圈依賴),bean A和bean B之間的迴圈依賴可以強制其中的一個bean優先注入另一個bean中,可以使其完全初始化(古老的雞/蛋場景)。
通常情況下你可以信任Spring去做正確的事情。在容器載入時它檢測配置問題,例如引用不存在的beans和迴圈依賴。當bean實際建立時,Spring設定屬性和解析依賴儘可能的晚。這意味著Spring容器正確載入但後面可能會產生異常,當你請求一個物件時,建立物件或它的某個依賴時出現問題,這時容器就會丟擲異常。例如,由於缺失或存在無效屬性,bean會丟擲異常。在真正需要這些beans之前建立它們,會花費一些前期時間和記憶體,當ApplicationContext
建立時你會發現配置問題,而不是在建立之後。為了單例bean懶惰初始化而不是預先例項化,你仍需要重寫這個預設行為。
如果沒有迴圈依賴存在,當一個或更多協作beans注入到一個獨立的bean中,在注入獨立bean之前,每個協作bean都是完全配置的。這意味著如果bean A有個依賴為bean B,Spring IoC容器在呼叫bean A的setter方法之前會完整的配置bean B。換句話說,bean被例項化(不是預先例項化的單例),設定依賴和相關的生命週期方法(例如配置初始化方法或初始化bean回撥方法)被呼叫。
依賴注入的例子
下面的例子使用基於XML的配置元資料進行setter注入。Spring XML配置檔案中的一小部分指定了一些bean的定義:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
在上面的例子中,setter宣告匹配XML檔案中指定的屬性。下面的例子使用了基於建構函式的依賴注入:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
bean定義中指定的建構函式引數將作為ExampleBean
的建構函式引數使用。
現在考慮這個例子的一個變種,不使用建構函式,而是Spring呼叫靜態工廠方法返回物件的一個例項:
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}
靜態工廠方法的引數通過<constructor-arg/>
元素提供,與建構函式使用的完全一樣。雖然這個例子中工廠方法返回值的型別與包含靜態工廠方法的類的型別一樣,但它們可以不一樣。工廠方法的例項(非靜態)的使用本質上樣式完全一樣(除了使用factory-bean
屬性代替class
屬性之外),因此這兒不討論這些細節。
3.4.2 依賴和配置的細節
正如上一節提到的那樣,你可以通過引用其它被管理bean(協作者)來定義bean的屬性和建構函式引數,或者在行內定義值。為了實現這個功能,Spring的基於XML的配置元資料在它的<property/>
和<constructor-arg/>
中支援子元素型別。
直接使用值 (基本型別,字串等等)
<property/>
元素的value
屬性指定了一個屬性或建構函式引數作為可讀的字串表示。使用Spring的轉換服務將這些值從String
轉成屬性或引數的真實型別。
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="masterkaoli"/>
</bean>
下面的例子為了更簡潔的XML配置使用了p名稱空間.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/>
</beans>
上面的XML是更簡潔的;然而,錯別字是在執行時發現而不是在設計時,除非你使用IDE例如IntelliJ IDEA
或Spring Tool Suite (STS)
,當你建立bean定義時它們支援自動的屬性補全。IDE輔助是強烈推薦的。
你也可以配置java.util.Properties
例項:
<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
Spring容器通過JavaBeans的PropertyEditor
機制將<value/>
元素內部的文字轉成java.util.Properties
例項。這是一個很好的捷徑,使用嵌入的<value/>
元素而不是使用value
屬性的方式,是Spring團隊支援的幾個地方之一。
idref元素
在容器中傳遞另一個bean的id(字串值,不是引用)到<constructor-arg/>
或<property/>
元素時,idref
元素是一種簡單的的誤差檢驗方式。
<bean id="theTargetBean" class="..."/>
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean" />
</property>
</bean>
上面的bean定義片段與下面的程式碼片段是等價的(執行時):
<bean id="theTargetBean" class="..." />
<bean id="client" class="...">
<property name="targetName" value="theTargetBean" />
</bean>
第一種形式優於第二種形式,因為idref
標籤允許容器在部署時驗證引用的bean,即命名的bean實際存在。在第二種形式中,當值傳給client
的targetName
時沒有進行驗證。拼寫錯誤只有在client
bean實際建立時才會發現(最可能有嚴重後果)。如果client
bean是原型bean,拼寫錯誤和產生的異常可能只有在容器部署很長時間之後才會發現。
idref
元素的local
屬性在4.0 beans xsd中不再支援,因為它不再為合格的bean引用提供值。簡單將你現有的idref local
引用改成idref bean
當更新到4.0 schema時。
<idref/>
元素帶來值的通常位置(至少在Spring 2.0之前)是在ProxyFactoryBean
bean定義中的AOP攔截器配置中。當你指定攔截器名字時使用<idref/>
元素來防止誤拼攔截器id。
其它bean的應用(協作bean)
ref
元素是<constructor-arg/>
或<property/>
定義元素的最終的元素。在這個元素中設定bean的指定屬性的值,值為容器管理的另一個bean(協作bean)的引用。引用的bean是設定屬性bean的依賴,在屬性設定之前引用bean需要進行初始化。(如果協作bean是一個單例模式的bean,它可能已經被容器初始化了。)所有引用bean根本上都是另一個物件的引用。作用域和驗證是根據你是否通過bean
,local
,或parent
屬性指定了另一個物件的id/name來決定的。
通過<ref/>
標籤的bean
屬性指定目標bean是最常用的形式,允許建立同容器或父容器中任何bean的引用,不管它是否是在同一個XML檔案中。bean
屬性的值可能與目標bean的id
屬性值相同,或與目標bean的name
屬性值相同。
<ref bean="someBean"/>
通過parent
屬性指定目標bean會引用當前容器的父容器中的bean。parent
屬性的值可能與目標bean的id
值或name
值相同,目標bean必須在當前容器的父容器中。當你有一個容器分層的時候你可以使用parent
,你想將現有bean包裹在有代理的父容器中且現有bean與父容器中的bean同名,你可以使用parent
屬性。
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
<!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
</property>
<!-- insert other configuration and dependencies as required here -->
</bean>
idref
元素的local
屬性在4.0 beans xsd中不再支援,因為它不再為合格的bean引用提供值。簡單將你現有的idref local
引用改成idref bean
當更新到4.0 schema時。
內部bean
<property/>
或<constructor-arg/>
元素內的<bean/>
元素中定義bean稱為內部bean。
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>
內部bean定義不要求定義id或name;如果指定了,容器不用用這個值作為識別符號。容器建立時也忽略scope
標記:內部bean總是匿名的且它們總是由外部bean建立。除了注入到封閉bean中或獨立的訪問它們,不可能將內部bean注入到協作bean中。
作為一種很少出現的情況,從特定的域中有可能會收到銷燬回撥函式,例如,對於請求域內的內部bean包含單例bean:內部bean例項的建立會繫結到它的包含bean,但銷燬回撥函式允許它進入到請求域的生命週期中。這不是一個常見的場景;內部bean通常簡單的共享它們的包含bean的作用域。
集合
在<list/>
,<set/>
,<map/>
和<props/>
元素中,你要分別設定Java Collection
型別list
,set
,map
和Properties
的屬性和引數。
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">[email protected]</prop>
<prop key="support">[email protected]</prop>
<prop key="development">[email protected]</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
map的key或value,或者是set value的值也可以是下面元素中的任何一個:
bean | ref | idref | list | set | map | props | value | null
集合合併
Spring也支援集合的合併。應用開發者可以定義父型別<list/>
,<map/>
,<set/>
或<props/>
元素,可以有繼承和覆蓋父集合的子型別元素<list/>
,<map/>
,<set/>
或<props/>
。也就是說,子集合的值是父集合和子集合中元素合併的結果,子集合元素覆蓋了父集合元素的值。
關於合併的這節討論了父子bean機制。對父子bean定義不熟悉的讀者可以去讀相關的章節。
下面的例子示範了集合合併:
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">[email protected]</prop>
<prop key="support">[email protected]</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">[email protected]</prop>
<prop key="support">[email protected]</prop>
</props>
</property>
</bean>
<beans>
注意child
bean定義中的adminEmails
屬性下的<props/>
元素使用了merge=true
屬性。當容器解析並例項化child
bean時,最終的例項含有adminEmails
Properties
集合,集合中的值是子adminEmails
集合和父adminEmails
集合合併的結果。
[email protected].com
[email protected].com
[email protected].co.uk
子Properties
集合的值繼承了父<props/>
中的所有屬性元素,子集合中的support
值覆蓋了父集合中的值。
<list/>
,<map/>
和<set/>
集合型別中的合併與上面類似。在特定的<list/>
元素情況下,關於List
集合型別的語義,也就是說,有序集合值的概念仍然是保留的;父list中的值領先於所有子list中的值。在Map
,Set
和Properties
集合型別,不存在順序。因此,無序語義在容器內部使用的集合型別Map
,Set
和Properties
的實現基礎上是有效的。
集合合併的限制
你不能合併不同的集合型別(例如Map
和List
),如果你試圖合併不同的集合型別會有適當的丟擲Exception
。merge
屬性必須在更低的、繼承的子定義中;在父集合定義中指定merge
屬性是多餘的並且不會進行合併。
強型別集合
隨著Java 5中泛型的引入,你可以使用強型別集合。也就是說,你可以宣告一個Collection
型別但它只能包含String
元素(例子)。如果你使用Spring將一個強型別的Collection
注入到bean中,你可以利用Spring的型別轉換支援,例如在將元素新增到Collection
之前,將你的強型別Collection
例項中的元素轉成恰當的型別。
public class Foo {
private Map<String, Float> accounts;
public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>
當注入foo
bean的accounts
屬性時,強型別Map<String, Float>
中元素型別的泛型資訊可以通過反射得到。因此Spring的型別轉換結構能識別各種值元素的型別為Float
,字串9.99, 2.75
和3.99
會被轉換成實際的Float
型別。
Null和空字串
Spring把屬性的空引數都處理為空Strings
。下面基於XML的配置元資料片段將email屬性設為空String
值(“”)。
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
上面的例子與下面的Java程式碼是等價的:
exampleBean.setEmail("")
<null/>
元素處理null
值。例如:
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
上面的配置與下面的Java程式碼等價。
exampleBean.setEmail(null)
XML 使用p名稱空間的縮寫
p名稱空間可以讓你不需要嵌入<property/>
元素便能使用bean
元素的屬性來描述你的屬性值以及/或協作beans。
Spring支援含有名稱空間的擴充套件配置形式,命名控制元件是基於XML Schema定義的。本章討論的beans配置形式是在XML Schema文件中定義的。但是p名稱空間不能在XSD檔案中定義並且只在Spring core中存在。
下面的例子顯示了兩個XML片段,解析結果是相同的:第一個是標準的XML形式,第二個使用了p名稱空間。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="classic" class="com.example.ExampleBean">
<property name="email" value="[email protected]"/>
</bean>
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="[email protected]"/>
</beans>
這個例子顯示了bean定義中p名稱空間中有個一個叫email的屬性。這會通知Spring包含屬性宣告。如前面所述,p名稱空間沒有schema定義,因此你可以將特性值(attribute)設到屬性值(property)上。
下面的例子包括兩個bean定義,且它們都引用了另一個bean:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
正如你所看到的,這個例子不僅包括使用了p名稱空間的屬性值,而且使用了一種特定的形式來宣告屬性引用。然而第一個bean定義使用<property name="spouse" ref="jane"/>
建立了一個從bean john
到bean jane
的引用,第二個bean定義使用p:spouse-ref="jane"
作為一個特性同樣定義了從bean john
到bean jane
的引用。在spouse
是屬性名的情況下,-ref
部分表示這不是一個直接的值而是另一個bean的引用。
p名稱空間不是標準的XML格式,例如,宣告的屬性引用會與以
Ref
結尾的