《Spring攻略(第2版)》——1.2 配置Spring IoC容器中的Bean
本節書摘來自異步社區《Spring攻略(第2版)》一書中的第1章,第1.2節,作者: 【美】Gary Mak , Josh Long , Daniel Rubio著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
1.2 配置Spring IoC容器中的Bean
1.2.1 問題
Spring提供了一個強大的IoC容器來管理組成應用的bean。為了利用容器服務,你必須配置運行於Spring IoC容器中的Bean。
1.2.2 解決方案
你可以通過XML文件、屬性文件、注釋甚至API來配置Spring IoC容器中的Bean。
Spring允許你在一個或者多個bean配置文件中配置bean。對於簡單的應用程序,可以在單個配置文件中集中配置bean。但是對於有許多bean的大型應用,你應該根據其功能(例如控製器、DAO和JMS)將它們分割到多個配置文件中。一種有益的分割方法是按照給定上下文所提供的架構層次進行分割。
1.2.3 工作原理
假定你打算開發一個生成序列號的應用程序,在這個應用程序中,為不同的用途可能生成許多係列的序列號,每個係列都有自己的前綴、後綴和初始值。因此,你必須在應用程序中創建和維護多個生成器實例。
創建Bean類
按照需求,你創建具有prefix、suffix和initial 3個屬性的SequenceGenerator類,這可以通過設值方法(Setter)或者構造程序注入。私有字段counter用於保存這個生成器的當前數值。每當你在一個生成器實例上調用getSequence()方法,都將得到加上前綴和後綴的最新序列號。將這個方法聲明為同步(synchronized),使其成為線程安全的方法。
package com.apress.springrecipes.sequence;
public class SequenceGenerator {
private String prefix;
private String suffix;
private int initial;
private int counter;
public SequenceGenerator() {}
public SequenceGenerator(String prefix, String suffix, int initial) {
this.prefix = prefix;
this.suffix = suffix;
this.initial = initial;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public void setInitial(int initial) {
this.initial = initial;
}
public synchronized String getSequence() {
StringBuffer buffer = new StringBuffer();
buffer.append(prefix);
buffer.append(initial + counter++);
buffer.append(suffix);
return buffer.toString();
}
}
正如你所看到的,這個SequenceGenerator類可以由取值/設值(getter/setter)方法或者構造程序配置。
使用容器進行配置時,這種方法被稱為構造程序注入和設值方法注入。
創建Bean配置文件
為了在Spring IoC容器中通過XML聲明bean,你首先必須創建一個具有合適名稱(如beans.xml)的XML bean配置文件。為了在IDE中更易於測試,可以將這個文件放置在classpath的根目錄下。
Spring配置XML使你能使用來自不同架構(tx、jndi、jee等)的自定義標記,讓bean的配置更加簡單和清晰。下麵是最簡單的XML配置的一個實例。
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
...
</beans>
在Bean配置文件中聲明bean
每個bean都應該提供一個唯一的名稱或者id,以及一個完全限定的類名,用來讓Spring IoC容器對其進行實例化。對於簡單類型的每個bean屬性(例如String和其他簡單類型),你可以為其指定一個元素。Spring會試圖將你指定的值轉換為該屬性的聲明類型。為了通過設值方法注入配置一個屬性,可使用元素,並在其name特性中指定屬性名稱。每個要求bean包含對應的一個設值方法。
<bean name="sequenceGenerator"
>
<property name="prefix">
<value>30</value>
</property>
<property name="suffix">
<value>A</value>
</property>
<property name="initial">
<value>100000</value>
</property>
</bean>
你也可以在元素中聲明,通過構造程序來配置Bean屬性。中沒有name屬性,因為構造程序參數是基於位置的。
<bean name="sequenceGenerator"
>
<constructor-arg>
<value>30</value>
</constructor-arg>
<constructor-arg>
<value>A</value>
</constructor-arg>
<constructor-arg>
<value>100000</value>
</constructor-arg>
</bean>
在Spring IoC容器中,每個Bean的名稱應該是唯一的。但是,如果裝入了超過一個上下文,允許重複的名稱覆蓋Bean聲明。Bean名稱可以由元素的name屬性定義。實際上,標識Bean的首選方法是:通過標準的XML id屬性,它的目的是標識XML文檔中的一個元素。這樣,如果你的文本編輯器是XML感知的,就能夠在設計時校驗每個Bean的唯一性。
<bean
>
...
</bean>
不過,XML在可以出現在XML id屬性中的字符上有限製。但是,你通常不會在Bean名稱中使用這些特殊字符。而且,Spring允許你在name屬性中為Bean指定多個以逗號分隔的名稱。但是你不能在id屬性中這麼做,因為這裏不允許逗號。
實際上,Bean名稱和Bean ID都是必需的。沒有定義名稱的Bean稱作匿名Bean。這樣的Bean通常隻用於與Spring容器交互,這種情況下將隻按類型注入,或者將會在一個外部Bean的聲明中嵌入它。
用簡寫定義Bean屬性
Spring支持指定簡單類型屬性值的一個簡寫。可以在元素中提供一個Value屬性代替包圍在元素中的屬性。
<bean
>
<property name="prefix" value="30" />
<property name="suffix" value="A" />
<property name="initial" value="100000" />
</bean>
這個簡寫也可用於構造程序參數。
<bean name="sequenceGenerator"
>
<constructor-arg value="30" />
<constructor-arg value="A" />
<constructor-arg value="100000" />
</bean>
從Spring 2.0開始,添加了另一個便利的定義屬性的簡寫。它使用p schema像元素中的屬性那樣定義Bean屬性。這可以縮短XML配置行。
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:p="https://www.springframework.org/schema/p"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean
p:prefix="30" p:suffix="A" p:initial="100000" />
</beans>
為Bean配置集合
List、Set和 Map是代表Java SDK中3種主要集合類型的核心接口,是Java Collections框架的一部分。對於每一種集合類型,Java用不同的函數和特性提供多種可選的實現。在Spring中,這些集合類型可以很輕鬆地用一組內建的XML標記(如、和)配置。
假定你打算允許序列生成器具有超過一個後綴。這些後綴將附加在序列號後麵,以連字號分隔。你可能考慮接受任意數據類型的後綴並在附加到序列號時轉換成字符串。
List、數組和Set
首先,我們使用java.util.List集合來包含你的後綴。List(列表)是一個排序並且索引的集合,它的元素可以通過索引號或者一個for-each循環訪問。
package com.apress.springrecipes.sequence;
...
public class SequenceGenerator {
...
private List<Object> suffixes;
public void setSuffixes(List<Object> suffixes) {
this.suffixes = suffixes;
}
public synchronized String getSequence() {
StringBuffer buffer = new StringBuffer();
...
for (Object suffix : suffixes) {
buffer.append("-");
buffer.append(suffix);
}
return buffer.toString();
}
}
為了在Bean配置中定義java.util.List接口的屬性,須指定一個包含元素的標記。標記中所允許的元素可以是由指定的常量值、指定的Bean引用、指定的內部Bean定義、指定的ID引用定義,或者指定的空元素。你甚至可以在一個集合中嵌入另一個集合。
<bean
>
<property name="initial" value="100000" />
<property name="suffixes">
<list>
<value>A</value>
<bean >
<constructor-arg value="http" />
<constructor-arg value="www.apress.com" />
<constructor-arg value="/" />
</bean>
<null />
</list>
</property>
</bean>
概念上,數組(Array)近似於List,它也是一個排序和索引的集合,可以用索引訪問。主要的不同在於數組的長度是固定的,不能動態擴展。在Java Collections框架中,數組和List可以通過Arrays.asList()和List.toArray()方法相互轉換。對於你的序列生成器,可以使用Object[]數組包含後綴,並且通過索引或者for-each循環訪問它們。
package com.apress.springrecipes.sequence;
...
public class SequenceGenerator {
...
private Object[] suffixes;
public void setSuffixes(Object[] suffixes) {
this.suffixes = suffixes;
}
...
}
Bean配置文件中的數組定義與標記指出的列表相同。
另一個常見的集合類型是Set。java.util.List接口和java.util.Set接口都擴展同一個接口:java.util.Collection。Set與List的不同在於既不排序也不索引,僅能存儲獨特的對象。這意味著Set中不能包含重複的元素。當相同的元素第二次被添加到一個Set時,它將替換舊的元素。相同的元素由equals()方法確定。
package com.apress.springrecipes.sequence;
...
public class SequenceGenerator {
...
private Set<Object> suffixes;
public void setSuffixes(Set<Object> suffixes) {
this.suffixes = suffixes;
}
...
}
為了定義java.util.Set類型的屬性,使用標記來定義元素,方法與List相同。
<bean
>
...
<property name="suffixes">
<set>
<value>A</value>
<bean >
<constructor-arg value="http" />
<constructor-arg value="www.apress.com" />
<constructor-arg value="/" />
</bean>
<null />
</set>
</property>
</bean>
盡管在原始的Set語義中沒有順序的概念,Spring還是使用java.util.LinkedHashSet保留了元素的順序,這是保留元素順序的java.util.Set接口的實現。
Map和Properties
Map接口是一個在關鍵字/值對中存儲項目的表。你可以通過關鍵字從Map中取得特定值,也可以使用for-each循環列舉Map項目。關鍵字和值可以是任何類型。關鍵字之間是否相等也由equals()方法確定。例如,你可以修改序列發生器,接受包含後綴的帶有關鍵字的java.util.Map集合。
package com.apress.springrecipes.sequence;
...
public class SequenceGenerator {
...
private Map<Object, Object> suffixes;
public void setSuffixes(Map<Object, Object> suffixes) {
this.suffixes = suffixes;
}
public synchronized String getSequence() {
StringBuffer buffer = new StringBuffer();
...
for (Map.Entry entry : suffixes.entrySet()) {
buffer.append("-");
buffer.append(entry.getKey());
buffer.append("@");
buffer.append(entry.getValue());
}
return buffer.toString();
}
}
在Spring中,Map由標記定義,帶有多個標記作為子項目。每個項目包含一個關鍵字和一個值。關鍵字必須在標記中定義。關鍵字和值的類型沒有限製,所以你可以為它們指定一個、、、或值。Spring也將使用java.util.LinkedHashMap保留Map項目的順序。
<bean
>
...
<property name="suffixes">
<map>
<entry>
<key>
<value>type</value>
</key>
<value>A</value>
</entry>
<entry>
<key>
<value>url</value>
</key>
<bean >
<constructor-arg value="http" />
<constructor-arg value="www.apress.com" />
<constructor-arg value="/" />
</bean>
</entry>
</map>
</property>
</bean>
可以用簡寫將關鍵字和值對用標記的屬性來定義。如果它們是簡單的常量,可以用key和value定義,如果它們是引用,可以用key-ref和value-ref定義。
<bean
>
...
<property name="suffixes">
<map>
<entry key="type" value="A" />
<entry key="url">
<bean >
<constructor-arg value="http" />
<constructor-arg value="www.apress.com" />
<constructor-arg value="/" />
</bean>
</entry>
</map>
</property>
</bean>
在目前見過的所有集合類中,都使用值來設置屬性。有時候,目標是使用Map實例配置一個Null值。Spring的XML配置方案包含對此的顯式支持。以下是帶有Null項目值的Map:
<property name="nulledMapValue">
<map>
<entry>
<key> <value>null</value> </key>
</entry>
</map>
</property>
java.util.Properties集合非常近似於Map。它也實現java.util.Map接口,並且以關鍵字/值對的形式存儲項目。唯一的不同是Properties集合的關鍵字和值始終是字符串。
package com.apress.springrecipes.sequence;
...
public class SequenceGenerator {
...
private Properties suffixes;
public void setSuffixes(Properties suffixes) {
this.suffixes = suffixes;
}
...
}
為了在Spring中定義java.util.Properties集合,使用標記,以多個標記作為子項目。每個標記必須定義一個key屬性並包含對應的值。
<bean
>
...
<property name="suffixes">
<props>
<prop key="type">A</prop>
<prop key="url">https://www.apress.com/</prop>
<prop key="null">null</prop>
</props>
</property>
</bean>
合並父Bean集合
如果你用繼承定義Bean,子Bean的集合可以通過設置Merge屬性為True與父Bean合並。對於集合,子元素將附加在父元素之後保持順序。所以,下麵的序列發生器將有4個後綴:A、B、A和C。
<beans ...>
<bean
>
<property name="prefixGenerator" ref="datePrefixGenerator" />
<property name="initial" value="100000" />
<property name="suffixes">
<list>
<value>A</value>
<value>B</value>
</list>
</property>
</bean>
<bean parent="baseSequenceGenerator">
<property name="suffixes">
<list merge="true">
<value>A</value>
<value>C</value>
</list>
</property>
</bean>
...
</beans>
對於或集合,如果值相同,子元素將覆蓋父元素。所以,下麵的序列發生器將有3個後綴:A、B和C。
<beans ...>
<bean
>
<property name="prefixGenerator" ref="datePrefixGenerator" />
<property name="initial" value="100000" />
<property name="suffixes">
<set>
<value>A</value>
<value>B</value>
</set>
</property>
</bean>
<bean parent="baseSequenceGenerator">
<property name="suffixes">
<set merge="true">
<value>A</value>
<value>C</value>
</set>
</property>
</bean>
...
</beans>
最後更新:2017-05-31 15:01:32