6.Spring容器中的Beanjava
6.1 Bean定義和Bean別名node
對於開發者來講,開發者使用Spring作2件事:(1)開發Bean (2)配置Bean。spring
<beans.../>標籤的屬性:數組
default-lazy-initsession
default-mergeide
default-autowire測試
default-autowire-condidatesui
default-init-methodthis
default-destroy-methodspa
<beans.../>的定義對每個bean都有效,<bean.../>對單個bean有效,若是這二者衝突,<bean.../>會覆蓋<beans.../>的定義。<bean.../>也能夠採用name屬性,用於指定Bean的別名,經過訪問Bean別名也能夠訪問Bean實例。
指定別名有2種方式:
(1)經過name屬性指定:能夠指定多個,使用逗號、冒號或者空格分隔多個別名;
(2)經過alias元素指定:一個元素是name,一個元素是alias。而且這個標籤,不是在<bean.../>裏,而是跟<bean.../>並列的。
新建一個測試類,AliasTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package
com.pmpa.ch03;
public
class
AliasTest {
private
String name;
private
int
age;
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
int
getAge() {
return
age;
}
public
void
setAge(
int
age) {
this
.age = age;
}
@Override
public
String toString() {
return
"AliasTest [name="
+ name +
", age="
+ age +
"]"
;
}
}
|
配置文件:
1
2
3
4
5
|
<
bean
id
=
"aliastest"
class
=
"com.pmpa.ch03.AliasTest"
name
=
"#221,iuffs,googke"
>
<
property
name
=
"name"
value
=
"honda"
></
property
>
<
property
name
=
"age"
value
=
"13"
></
property
>
</
bean
>
<
alias
name
=
"googke"
alias
=
"Sun"
></
alias
>
|
測試類BeanTest.java
1
2
|
AliasTest at = ctx.getBean(
"Sun"
, AliasTest.
class
);
System.out.println(at);
|
運行結果:
1
|
AliasTest [name=honda, age=13]
|
能夠看到,經過別名「Sun」,Spring也找到了這個Bean,並正常輸出。
6.2 容器中Bean的做用域
Bean包含5種做用域:
singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的Bean將只有一個實例
prototype:原型模式,每次經過容器的getBean方法獲取prototype定義的Bean時,都將產生一個新的Bean實例
request:對於每次HTTP請求,使用request定義的Bean都將產生一個新實例,即每次HTTP請求將會產生不一樣的Bean實例。只有在Web應用中使用Spring時,該做用域纔有效
session:對於每次HTTP Session,使用session定義的Bean豆漿產生一個新實例。一樣只有在Web應用中使用Spring時,該做用域纔有效
globalsession:每一個全局的HTTP Session,使用session定義的Bean都將產生一個新實例。典型狀況下,僅在使用portlet context的時候有效。一樣只有在Web應用中使用Spring時,該做用域纔有效
6.2.1 singleton和prototype做用域測試實例:
若是不指定Bean的做用域,默認是singleton。prototype做用域的Bean的建立、銷燬的系統代價比較大,應儘可能避免使用。Spring配置文件的<bean.../>標籤經過scope屬性來配置bean的做用域。
ScopeTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package
com.pmpa.ch03;
public
class
ScopeTest {
private
String idNo;
public
String getIdNo() {
return
idNo;
}
public
void
setIdNo(String idNo) {
this
.idNo = idNo;
}
@Override
public
String toString() {
return
"ScopeTest [idNo="
+ idNo +
"]"
;
}
}
|
配置文件 beans03.xml
1
2
3
4
5
6
|
<
bean
id
=
"scope1"
class
=
"com.pmpa.ch03.ScopeTest"
scope
=
"prototype"
>
<
property
name
=
"idNo"
value
=
"152301ABC"
></
property
>
</
bean
>
<
bean
id
=
"scope2"
class
=
"com.pmpa.ch03.ScopeTest"
scope
=
"singleton"
>
<
property
name
=
"idNo"
value
=
"110332CDE"
></
property
>
</
bean
>
|
測試類BeanTest.java:
1
2
3
4
5
6
7
8
9
|
//prototype做用域Bean的測試:
ScopeTest st_prototype_1 = ctx.getBean(
"scope1"
, ScopeTest.
class
);
ScopeTest st_prototype_2 = ctx.getBean(
"scope1"
, ScopeTest.
class
);
System.out.println(
"prototype類型的對象1和2是不是同一對象:"
+ (st_prototype_1 == st_prototype_2));
//sigleton做用域Bean的測試:
ScopeTest st_singleton_1 = ctx.getBean(
"scope2"
, ScopeTest.
class
);
ScopeTest st_singleton_2 = ctx.getBean(
"scope2"
, ScopeTest.
class
);
System.out.println(
"sigleton類型的對象1和2是不是同一對象:"
+ (st_singleton_1 == st_singleton_2));
|
運行結果:
1
2
|
prototype類型的對象1和2是不是同一對象:false
sigleton類型的對象1和2是不是同一對象:true
|
對於singleton做用域的Bean,每次請求該id的bean,都將返回同一個共享實例。
6.2.2 request、session、globalsession做用域測試實例(待補充):
6.3 配置依賴
BeanFactory和ApplicationContext實例化容器中Bean的時機不一樣:前者等到程序須要Bean實例時才建立;後者在容器建立ApplicationContext實例時,會初始化容器中的全部singleton Bean。
ApplicationContext能夠在容器初始化階段檢驗出配置錯誤。
Spring容許經過以下元素爲setter方法、構造器參數指定參數值。
value
ref
bean
list、set、map、props
以上4種狀況分別表明Bean類的4種類型的成員變量。
6.3.1 value:
value元素用於指定基本類型及其包裝、字符串類型的參數。前邊講述的內容中,包含不少value賦值的情形,這裏再也不贅述。
6.3.2 ref:
若是須要爲Bean設置的屬性值是容器中的另外一個Bean實例,則應該使用<ref.../>元素(其實,經常使用的是ref屬性,前邊事例都是這樣。)這種術語也叫做「注入合做者Bean」。ref實例在前邊例子中也不少,再也不贅述。
自動裝配注入合做者Bean:
Spring能自動裝配Bean與Bean之間的依賴關係,即無需使用ref顯示指定依賴Bean,而是由Spring容器檢查XML配置文件內容,根據某種規則,爲調用者Bean注入被依賴的Bean。
自動裝配經過<beans.../>的default-autowrite屬性指定,也能夠經過<bean.../>元素的autowire屬性指定。
自動裝配能夠減小配置文件的工做量,可是下降了依賴關係的透明性和清晰性。
auto-wire屬性的可用值:
no:不使用自動裝配,這是默認配置。
byName:根據setter方法名進行自動裝配。
byType:根據setter方法的形參類型來自動裝配。若是找到多個和形參類型匹配的Bean,則拋出異常。
constructor:與byType相似,區別是用於自動匹配構造器的參數。
autodetect:Spring容器根據Bean內部結構,自行決定使用constructor或byType策略。
AutoWireTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package
com.pmpa.ch03;
public
class
AutoWireTest {
private
String uName;
private
Car car;
private
House house;
public
String getuName() {
return
uName;
}
public
void
setuName(String uName) {
this
.uName = uName;
}
public
Car getCar() {
return
car;
}
public
void
setCar(Car car) {
this
.car = car;
}
public
House getHouse() {
return
house;
}
public
void
setHouse(House house) {
this
.house = house;
}
public
void
testAutoWire()
{
System.out.println(
"begin testing autowire "
+ uName);
car.carTest();
house.testHouse();
}
}
|
在外部,建立2個類,分別是Car和House,這2個類很簡單。
配置文件beans03.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<
bean
id
=
"benz"
class
=
"com.pmpa.ch03.Car"
>
<
property
name
=
"brand"
value
=
"benz"
></
property
>
<
property
name
=
"speed"
value
=
"270"
></
property
>
</
bean
>
<
bean
id
=
"biguiyuan"
class
=
"com.pmpa.ch03.House"
>
<
property
name
=
"address"
value
=
"GuoMao"
></
property
>
<
property
name
=
"price"
value
=
"78900000"
></
property
>
</
bean
>
<
bean
id
=
"autowiretest"
class
=
"com.pmpa.ch03.AutoWireTest"
autowire
=
"byType"
>
<
property
name
=
"uName"
value
=
"自動裝配測試1"
></
property
>
</
bean
>
|
這時候,建立的2個bean實例benz和biguiyuan的類型分別是Car和House,因此若是autowiretest實例的autowire屬性設置爲byType,則能夠自動裝配這2個屬性。
測試類BeanTest.java
1
2
|
AutoWireTest awt = ctx.getBean(
"autowiretest"
, AutoWireTest.
class
);
awt.testAutoWire();
|
運行結果:
1
2
3
|
begin testing autowire 自動裝配測試1
The car benz can reach the speed of 270.0
The house GuoMao can reach the price of 7.89E7
|
經過byType自動裝配成功。若是想經過byName完成自動裝配,則須要修改配置文件,修改Car和House類型的2個bean實例的id(修改爲什麼Id,須要根據類AutoWireTest的2個setter方法來肯定,應該是car和house),修改以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<
bean
id
=
"car"
class
=
"com.pmpa.ch03.Car"
>
<
property
name
=
"brand"
value
=
"benz"
></
property
>
<
property
name
=
"speed"
value
=
"270"
></
property
>
</
bean
>
<
bean
id
=
"house"
class
=
"com.pmpa.ch03.House"
>
<
property
name
=
"address"
value
=
"GuoMao"
></
property
>
<
property
name
=
"price"
value
=
"78900000"
></
property
>
</
bean
>
<
bean
id
=
"autowiretest"
class
=
"com.pmpa.ch03.AutoWireTest"
autowire
=
"byName"
>
<
property
name
=
"uName"
value
=
"自動裝配測試1"
></
property
>
</
bean
>
|
當一個Bean既使用自動裝配依賴,又使用ref顯示指定依賴時,則顯示指定的依賴會覆蓋自動裝配的依賴。
6.3.3 注入嵌套Bean:
若是某個Bean所依賴的Bean不想被Spring容器直接訪問,可使用嵌套Bean。
把<bean.../>配置成<property.../>或者<constructor-args.../>的子元素,那麼該<bean.../>元素配置的Bean僅僅做爲setter注入、構造注入的參數,這種Bean就是嵌套Bean。因爲容器不能獲取嵌套Bean,所以不須要爲嵌套Bean指定id元素。
注意以下的配置文件:
1
2
3
4
5
|
<
bean
id
=
"chinese02"
class
=
"com.pmpa.ch02.Chinese"
>
<
property
name = "axe" >
<
bean
class
=
"com.pmpa.ch02.SteelAxe"
></
bean
>
</
property
>
</
bean
>
|
使用嵌套Bean與使用ref引用容器中另外一個Bean在本質上是同樣的。 以前的寫法是<property name="..." ref="..."/>,如今改爲<property name="..."><bean class="..."/></property>。注意這種寫法中,不能指定bean id屬性。
6.3.4 注入集合值:
若是須要調用形參類型爲集合的setter方法,或者調用形參類型爲集合的構造器,則可以使用集合元素<list.../>、<set.../>、<map.../>、<props.../>分別來設置類型爲List、Set、Map和Properties的集合參數值。
下面定義一個包含集合屬性的java類,Student.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
package
com.pmpa.ch03;
import
java.util.Arrays;
import
java.util.List;
import
java.util.Map;
import
java.util.Properties;
import
java.util.Set;
public
class
Student {
private
String name;
private
List<String> schools;
private
Map scores;
private
Map<String,Car> carPhases;
private
Properties examinations;
private
Set infos;
private
String[] careers;
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
List<String> getSchools() {
return
schools;
}
public
void
setSchools(List<String> schools) {
this
.schools = schools;
}
public
Map getScores() {
return
scores;
}
public
void
setScores(Map scores) {
this
.scores = scores;
}
public
Map<String, Car> getCarPhases() {
return
carPhases;
}
public
void
setCarPhases(Map<String, Car> carPhases) {
this
.carPhases = carPhases;
}
public
Properties getExaminations() {
return
examinations;
}
public
void
setExaminations(Properties examinations) {
this
.examinations = examinations;
}
public
Set getInfos() {
return
infos;
}
public
void
setInfos(Set infos) {
this
.infos = infos;
}
public
String[] getCareers() {
return
careers;
}
public
void
setCareers(String[] careers) {
this
.careers = careers;
}
@Override
public
String toString() {
return
"Student [name="
+ name +
", schools="
+ schools +
", scores="
+ scores +
", carPhases="
+ carPhases
+
", examinations="
+ examinations +
", infos="
+ infos +
", careers="
+ Arrays.toString(careers) +
"]"
;
}
}
|
分別爲<property.../>元素增長<list.../>、<set.../>、<map.../>、<props.../>子元素來配置這些集合類型的參數值。
配置文件定義bean03.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
<
bean
id
=
"toyota"
class
=
"com.pmpa.ch03.Car"
>
<
property
name
=
"brand"
value
=
"toyota"
></
property
>
<
property
name
=
"speed"
value
=
"180"
></
property
>
</
bean
>
<
bean
id
=
"bmw"
class
=
"com.pmpa.ch03.Car"
>
<
property
name
=
"brand"
value
=
"bmw"
></
property
>
<
property
name
=
"speed"
value
=
"270"
></
property
>
</
bean
>
<
bean
id
=
"bently"
class
=
"com.pmpa.ch03.Car"
>
<
property
name
=
"brand"
value
=
"bently"
></
property
>
<
property
name
=
"speed"
value
=
"460"
></
property
>
</
bean
>
<
bean
id
=
"student"
class
=
"com.pmpa.ch03.Student"
>
<
property
name
=
"name"
value
=
"Tom White"
></
property
>
<!-- 配置List集合類型 -->
<
property
name
=
"schools"
>
<
list
>
<!-- 下面每一個value標籤,定義一個list的元素,根據list泛型的類型,也可使用ref、bean等等標籤 -->
<
value
>衡水一中</
value
>
<
value
>北京工業大學</
value
>
<
value
>斯坦福大學</
value
>
</
list
>
</
property
>
<!-- 配置Map集合類型 -->
<
property
name
=
"scores"
>
<
map
>
<!-- 下面每一個entry標籤,定義一個map的元素,也就是key-value對 -->
<
entry
key
=
"數學"
value
=
"78"
></
entry
>
<
entry
key
=
"語文"
value
=
"132"
></
entry
>
<
entry
key
=
"英語"
value
=
"104"
></
entry
>
</
map
>
</
property
>
<!-- 配置Map集合類型,Map<String,Car> -->
<
property
name
=
"carPhases"
>
<
map
>
<!-- 下面每一個entry標籤,定義一個map的元素,也就是key-value對 -->
<
entry
key
=
"打工階段"
value-ref
=
"toyota"
></
entry
>
<
entry
key
=
"創業階段"
value-ref
=
"bmw"
></
entry
>
<
entry
key
=
"成功階段"
value-ref
=
"bently"
></
entry
>
</
map
>
</
property
>
<!-- 配置Properties集合屬性 -->
<
property
name
=
"examinations"
>
<
props
>
<!-- 每一個prop元素都配置一個屬性項,其中key指定屬性名 -->
<
prop
key
=
"血常規"
>正常</
prop
>
<
prop
key
=
"尿常規"
>脂肪肝</
prop
>
</
props
>
</
property
>
<!-- 配置Set集合屬性 -->
<
property
name
=
"infos"
>
<
set
>
<!-- 每一個value、ref、bean都配置一個Set元素 -->
<
value
>簡單的字符串賦值</
value
>
<
bean
class
=
"com.pmpa.ch03.House"
></
bean
>
<
ref
bean
=
"car"
></
ref
>
<!-- Set裏能夠繼續配置一個List -->
<
list
>
<
value
>Set中的List</
value
>
<
set
><
value
type
=
"java.lang.String"
>List中的Set</
value
></
set
>
</
list
>
</
set
>
</
property
>
<!-- 配置數組集合類型 -->
<
property
name
=
"careers"
>
<
list
>
<!-- 每一個value標籤是一個數組元素 -->
<
value
>IBM</
value
>
<
value
>Google</
value
>
<
value
>Oracle</
value
>
</
list
>
</
property
>
</
bean
>
|
測試類BeanTest.java
1
2
3
|
System.out.println(
"-----------------------測試集合注入------------------------"
);
Student student = ctx.getBean(
"student"
, Student.
class
);
System.out.println(student);
|
運行結果:
1
2
|
-----------------------測試集合注入------------------------
Student [name=Tom White, schools=[衡水一中, 北京工業大學, 斯坦福大學], scores={數學=78, 語文=132, 英語=104}, carPhases={打工階段=Car [brand=toyota, speed=180.0], 創業階段=Car [brand=bmw, speed=270.0], 成功階段=Car [brand=bently, speed=460.0]}, examinations={血常規=正常, 尿常規=脂肪肝}, infos=[簡單的字符串賦值, com.pmpa.ch03.House@6d7b4f4c, Car [brand=benz, speed=270.0], [Set中的List, [List中的Set]]], careers=[IBM, Google, Oracle]]
|
說明:
Spring對List元素和數組的處理是一致的,都使用<list.../>元素配置。
<list.../>、<set.../>、<map.../>、<props.../>又能夠接受value;ref;bean;list、set、map、props元素。
Properties類型,其key和value只能是字符串。
<entry.../>元素支持4個屬性:key、key-ref(key爲容器中的其餘bean實例)、value、value-ref(value爲容器中的其餘bean實例,上邊例子中有說明)。
Spring容器支持集合的合併,子Bean中的結合屬性值能夠從其父Bean的集合屬性繼承和覆蓋而來。
6.3.4 組合屬性:
使用car.brand 等相似格式爲對象成員變量的屬性賦值。爲Bean的組合屬性設置參數時,除最後一個屬性外,其餘屬性值都不容許爲null。
CombinationTest.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package
com.pmpa.ch03;
public
class
CombinationTest {
private
String name;
private
Car car =
new
Car();
private
House house =
new
House();
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
Car getCar() {
return
car;
}
public
void
setCar(Car car) {
this
.car = car;
}
public
House getHouse() {
return
house;
}
public
void
setHouse(House house) {
this
.house = house;
}
@Override
public
String toString() {
return
"CombinationTest [name="
+ name +
", car="
+ car +
", house="
+ house +
"]"
;
}
}
|
配置文件:
1
2
3
4
5
6
|
<
bean
id
=
"combinationtest"
class
=
"com.pmpa.ch03.CombinationTest"
>
<
property
name
=
"name"
value
=
"combination"
/>
<
property
name
=
"car.brand"
value
=
"honda"
></
property
>
<
property
name
=
"car.speed"
value
=
"193"
></
property
>
<
property
name
=
"house.address"
value
=
"XiDan"
></
property
>
</
bean
>
|
運行結果:
1
2
|
-----------------------測試組合屬性------------------------
CombinationTest [name=combination, car=Car [brand=honda, speed=193.0], house=House [address=XiDan, price=0.0]]
|
使用組合屬性指定參數值時,除了最後一個屬性外,其餘屬性都不能爲Null,不然將引起NullPointerException異常,若是感受自學困難的話,能夠去參加java培訓來提升。例如,上面配置文件爲car.brand指定參數值,則CombinationTest的car就必定不能爲null,因此該類在設置private屬性時,要直接new一個對象。是這樣:private Car car = new Car();而不能是這樣:private Car car;
這種賦值方法,底層spring執行時,須要先調用getter方法,再調用setter方法,
1
|
combinationtest.getCar().setBrand(
"honda"
);
|
也就是說組合屬性只有最後一個屬性才調用setter方法,其他都調用getter方法——這也是爲何前面屬性都不能爲null的原因。