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的原因。