Spring 学习笔记之 Bean 的属性注入

通常,JavaBean 的属性值是私有的,同时拥有一组存取器方法,以 setXxx () 和 getXxx () 的形式存在。Spring 通过 Java 的反射机制借助属性的 set 方法来配置属性的值,以实现 setter 方式的注入。

下面定义一个 Person 类来演示 Spring Bean 的属性注入:

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
package cn.javacodes.spring.beans;
/**
* Created by eric on 16-8-31.
*/
public class Person {
private String name;
private int age;
private String sex;
private String resume;
private Car car;

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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getResume() {
return resume;
}
public void setResume(String resume) {
this.resume = resume;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Person(String name, int age, String sex, String resume) {
this.name = name;
this.age = age;
this.sex = sex;
this.resume = resume;
}
public Person(String name, int age, String sex, String resume, Car car) {
this.name = name;
this.age = age;
this.sex = sex;
this.resume = resume;
this.car = car;
}
public Person() {
}
}

Person 类中包含一个无参的构造方法,因此可以在 Spring 中使用下面的 XML 声明一个

1
<bean id="person" class="cn.javacodes.spring.beans.Person"></bean>

通过上面的 XML 生命的没有对任何属性进行赋值,下面逐步讲解如何为属性进行赋值。

一、注入简单值(字面值)

1、通过 value 属性注入

在 Spring 中可以使用元素配置 Bean 的属性。例如现在想给 bean person 的姓名、年龄、性别、简历等属性赋值,那么可以使用下面的方式:

1
2
3
4
5
6
<bean id="person" class="cn.javacodes.spring.beans.Person">
<property name="name" value="张三"></property>
<property name="age" value="16"></property>
<property name="sex" value="男"></property>
<property name="resume" value="疯狂的Java码农"></property>
</bean>

一旦 Person 被实例化,Spring 就会调用元素所指定属性的 setter 方法为该属性注入值。在本实例中,name、sex、resume 属性均为 String 类型的字符串,而我们发现 int 类型的 age 属性也可以成功注入简单值,这是因为元素并没有限制只能注入 String 类型的值,value 属性同样可以指定为基本数据类型(包括数值型和布尔型等)。

2、通过子元素注入

那么我们想象一下,如果我们想注入的值中包含 XML 的特殊字符怎么办,上述方法是否仍然奏效?不妨尝试一下,例如我想将 person 的 resume 属性注入值 “<Coder!>”,那么在这个值中,“<>” 在 XML 中拥有特殊的含义,如果仍然使用 value 属性进行赋值,将会出现异常!

Exception in thread "main" org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 11 in XML document from class path resource [applicationContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 11; columnNumber: 40; 与元素类型 "property" 相关联的 "value" 属性值不能包含 '<' 字符。

那么如何解决这个问题呢?元素中可以添加一个子元素,其作用与 value 属性基本相同,而当我们想要注入包含特殊字符的字面值时,可以在子元素中使用 CDATA 节来解决,例如:

1
2
3
4
5
6
7
8
<bean id="person" class="cn.javacodes.spring.beans.Person">
<property name="name" value="张三"></property>
<property name="age" value="16"></property>
<property name="sex" value="男"></property>
<property name="resume">
<value><![CDATA[<Coder!>]]></value>
</property>
</bean>

二、引用其他 Bean

上面使用 value 属性或子元素注入简单值只是实现简单的硬编码,而在实际开发过程中,经常需要为某一个属性赋值为一个对象,通常为另一个 bean。那么如何在 Spring 中为属性引用其他 bean 呢?

例如本例中 Person 实例拥有一个 Car 类型的属性 car,可以使用元素的 ref 属性为其进行赋值。类 Car 的源码如下:

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
package cn.javacodes.spring.beans;
/**
* Created by huzha on 2016-09-06.
*/
public class Car {
private String brand;
private String model;
private String color;
public Car() {
}
public Car(String brand, String model, String color) {
this.brand = brand;
this.model = model;
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", model='" + model + ''' +
", color='" + color + ''' +
'}';
}
}

我们首先在 XML 中声明一个 Car 类型的,源码如下:

1
2
3
4
5
<bean id="car" class="cn.javacodes.spring.beans.Car">
<property name="brand" value="奥迪"></property>
<property name="model" value="A6"></property>
<property name="color" value="黑色"></property>
</bean>

那么可以在 person bean 中引用这个 bean,源码如下:

1
2
3
4
5
6
7
8
9
<bean id="person" class="cn.javacodes.spring.beans.Person">
<property name="name" value="张三"></property>
<property name="age" value="16"></property>
<property name="sex" value="男"></property>
<property name="resume">
<value><![CDATA[<Coder!>]]></value>
</property>
<property name="car" ref="car"></property>
</bean>

三、注入内部 Bean

我们知道,在 Spring 中 bean 的默认作用域是单例的,通过上述方式注入 bean,意味着这个 bean 还可能被其他 bean 引用。例如,可能有另外一个 Person 类型的 bean person1,其 car 属性同样引用的是 id 为 car 的 bean,那么意味着这两个人同时拥有同一台奥迪 A6,这通常是不现实的。那么如何为一个 Bean 创建一个独有的内部 bean 呢?

在 Spring 中元素中还可以嵌套其它的元素,我们称嵌套在内部的为内部 bean,这个概念与 Java 的内部类十分相似。

我们修改一下 person 的声明,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="person" class="cn.javacodes.spring.beans.Person">
<property name="name" value="张三"></property>
<property name="age" value="16"></property>
<property name="sex" value="男"></property>
<property name="resume">
<value><![CDATA[<Coder!>]]></value>
</property>
<property name="car">
<bean class="cn.javacodes.spring.beans.Car">
<property name="brand" value="奥迪"></property>
<property name="model" value="A6"></property>
<property name="color" value="黑色"></property>
</bean>
</property>
</bean>

这样我们就成功的注入了一个内部 Bean。细心的朋友可能会发现,在这里内部 bean 没有给定其 id 属性,因为在这里其 id 属性似乎并不是那么必要,因为我们永远不会通过 id 来引用内部 bean。这也突出了内部 Bean 的最大缺点:它们不能被复用。内部 Bean 仅适用于一次注入,而且也不能被其它 Bean 所引用。

内部 Bean 并不局限于在元素中使用,我们同样可以在中使用,例如我们修改 person 的声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="person" class="cn.javacodes.spring.beans.Person">
<constructor-arg value="张三"></constructor-arg>
<constructor-arg value="16"></constructor-arg>
<constructor-arg name="sex" value="男"></constructor-arg>
<constructor-arg >
<value><![CDATA[<Coder!>]]></value>
</constructor-arg>
<constructor-arg>
<bean class="cn.javacodes.spring.beans.Car">
<constructor-arg name="brand" value="奥迪"></constructor-arg>
<constructor-arg name="model" value="A6"></constructor-arg>
<constructor-arg name="color" value="黑色"></constructor-arg>
</bean>
</constructor-arg>
</bean>