# Spring教程 - 4 IoC容器 - 基于XML配置(1)
# 4.1 获取Bean
在介绍 IoC 之前,先介绍一下获取 bean 的方式。
# 1 通过id获取bean
在 HelloWorld 程序中,因为已经在 XML 中指定了 bean 标签的 id,所以可以根据 id 获取到 bean 对象。
如下:
// 1. 加载spring的配置文件,在类路径下加载,类路径指的是src/main/resources
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 2. 通过ID获取配置创建的对象
UserService userService = (UserService) context.getBean("userService");
// 3. 调用对象的方法
userService.getUser();
2
3
4
5
6
# 2 通过类型获取bean
我们还可以通过类型获取 bean,通过 UserService 类型获取 bean。
举个栗子:
// 1. 加载spring的配置文件,在类路径下加载,类路径指的是src/main/resources
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 2. 从spring容器中获取对象
UserService userService = (UserService) context.getBean(UserService.class); // 通过类型获取
// 3. 调用对象的方法
userService.getUser();
2
3
4
5
6
- 在上面额代码中,通过
UserService.class
类型获取 bean 对象。
但是这里需要注意,通过类型获取 bean,需要保证 IoC 容器中该类型的 bean 只能有一个。
例如我们使用相同的类型,配置了两个 bean:
<bean id="userService1" class="com.foooor.hellospring.UserService">
</bean>
<bean id="userService2" class="com.foooor.hellospring.UserService">
</bean>
2
3
4
5
那么在通过类型获取 bean 的时候,就会报错,提示不唯一,因为或找到多个 bean。
如果一个类继承了一个接口,那么我们也可以通过接口来获取到 bean:
例如,UserService 实现了 IUserService 接口,那么可以通过如下方式获取:
IUserService userService = context.getBean(IUserService.class); // 通过类型获取
但是需要注意,如果接口有多个实现类,那么就不可以通过这种方式获取了,因为 bean 又不唯一了。
# 3 通过id和类型获取bean
还可以同时通过 id 和类型获取 bean 对象:
// 1. 加载spring的配置文件,在类路径下加载,类路径指的是src/main/resources
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 2. 从spring容器中获取对象
UserService userService = context.getBean("userService", UserService.class); // 通过id和类型获取
// 3. 调用对象的方法
userService.getUser();
2
3
4
5
6
这种方式比直接通过 id 获取 bean 的好处就是不用进行强制类型转换,获取到的就是 UserService
类型。
推荐用这种方式。
# 4.2 setter实现依赖注入
下面开始介绍如何实现依赖注入。
我们在前面讲了,通过依赖注入(DI)实现 IoC,而依赖注入就是通过构造器或者setter方法,设置 bean 中的属性。
下面就演示一下如果实现 bean 中属性的自动注入,也就是自动赋值。
# 1 创建类
假设有一个学生类,我这里定义 Student 类如下:
package com.foooor.hellospring.pojo;
public class Student {
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 "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
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
- 类很简单,就是有两个属性,name 和 age;
- 注意,上面的属性,一定要有 setter 方法,Spring 会调用 setter 对属性进行赋值。
下面通过 Spring 创建 Student 实例 bean,并实现属性的自动注入。
# 2 配置bean
在 bean.xml 中配置 Student bean,如下:
<!-- 配置Student对象的创建 -->
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<!-- 为属性赋值 -->
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
2
3
4
5
6
- 在配置文件中,在
<bean>
中,通过<property>
为类中的属性进行赋值,这样 Spring 在创建对象的时候,会自动将值注入到 bean 对象中。 - 需要注意,
name="age"
并不是直接给 age 赋值,而是会调用setAge()
方法,也就是调用set + age(首字母大写)
的方法。
# 3 测试
创建一个测试类,然后通 Spring IoC 容器中获取 Student bean 对象:
package com.foooor.hellospring;
import com.foooor.hellospring.pojo.Student;
import com.foooor.hellospring.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentTest {
@Test
public void testStudent() {
// 1. 加载spring的配置文件,在类路径下加载,类路径指的是src/main/resources
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 2. 获取spring容器中创建的对象
Student student = context.getBean("student", Student.class);
// 3. 打印对象
System.out.println(student);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 从 Spring IoC 容器中获取到 Student bean,打印对象信息,可以看到获取到了配置的信息。
执行结果:
Student{name='张三', age=18}
通过 setter 实现依赖注入,在开发中使用的是最多的。
# 4.3 构造器实现依赖注入
下面介绍使用构造方法来实现属性的自动注入。
# 1 创建类
还是定义 Student 类,并在类中定义构造方法。
package com.foooor.hellospring.pojo;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- Spring 会调用构造方法,为 bean 中的属性赋值。
# 2 配置bean
<!-- 配置Student对象的创建 -->
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<!-- 通过构造器为属性赋值 -->
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="18"/>
</bean>
2
3
4
5
6
- 在
<bean>
标签中,通过<constructor-arg>
为构造方法中的参数传递值;
<constructor-arg>
还有一个 index
属性,可以通过参数的索引指定参数的值:
<constructor-arg index="0" value="张三"/>
- 用的不多,一般通过 name 就好了。
# 3 测试
和上面的测试方法是一样的:
package com.foooor.hellospring;
import com.foooor.hellospring.pojo.Student;
import com.foooor.hellospring.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentTest {
@Test
public void testStudent() {
// 1. 加载spring的配置文件,在类路径下加载,类路径指的是src/main/resources
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 2. 获取spring容器中创建的对象
Student student = context.getBean("student", Student.class);
// 3. 打印对象
System.out.println(student);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 依然可以从 Spring IoC 容器中获取到 Student bean,打印对象信息。
# 4.4 不同类型数据的注入
我们已经学会了依赖注入的方式,但是在依赖注入时,不同的数据类型,注入的配置有一些区别,我们来看一下。
# 1 基本类型
例如字符串、整形、布尔值、浮点等类型,就像上面演示的一样,直接通过 <property name="name" value="value">
标签的方式注入即可。
Student.java
public class Student {
private String name;
private int age;
private double height;
private boolean gender;
// ...getters and setters.
}
2
3
4
5
6
7
8
9
10
bean配置:
<!-- 配置Student对象的创建 -->
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<!-- 通过setter方法为属性赋值 -->
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="height" value="1.88"/>
<property name="gender" value="true"/>
</bean>
2
3
4
5
6
7
8
- 在类中,基本数据类型也可以定义为包装类,例如 Integer,只是如果是基本类型的话,不注入值的话,默认为0(整形默认为0,boolean默认为false),如果是包装类的话,不注入值,默认为null。
但是在注入字符串的时候可能存在一个问题,就是字符串中包含特殊字符,例如 <、>
这样的特殊字符,就不能在 xml 中直接写,需要使用实体字符来代替。
举个栗子:
<!-- 报错 -->
<property name="name" value="<张三>"/>
<!-- 特殊字符需要使用实体字符代替 -->
<property name="name" value=">张三<"/>
<!--
> -- >
< -- <
& -- &
" -- "
' -- '
-->
2
3
4
5
6
7
8
9
10
11
12
13
如果不想使用实体字符来代替,那么可以使用 CDATA节
。
举个栗子:
<property name="name">
<value><![CDATA[这里面的内容,<牛逼>,不会被解析成标签或实体]]></value>
</property>
2
3
- CDATA 只是XML 的语法糖,CDATA 里可以写任意字符,但 不能包含
]]>
,因为它是 CDATA 的结束符。如果确实要写,可以拆成两个CDATA,例如第一个CDATA包含前半部分]]
,后一个CDATA包含后半部分>
,变成:<![CDATA[]]]]><![CDATA[>]]>
。 - 用的不多,我是没用过。
# 2 对象类型
对象类型应该是最常用的类型了,因为在实际的开发中,基本上都会对项目进行分层,例如使用 Service 调用 Dao 等。
这里创建一个 Clazz (班级类,class是关键字,使用clazz),在 Clazz 中注入 Student ,如下:
Clazz.java
package com.foooor.hellospring.pojo;
public class Clazz {
// 班级名称
private String name;
// ...getters and setters
}
2
3
4
5
6
7
8
9
Student.java
package com.foooor.hellospring.pojo;
public class Student {
private String name;
private int age;
// 班级
private Clazz clazz;
// ...getters and setters
}
2
3
4
5
6
7
8
9
10
11
- 上面定义了 Student 类,在 Student 类中,注入 Clazz 类。
下面通过配置 bean.xml 实现两个类对象的实例化,以及属性的依赖注入。
方式一:引用外部bean
在配置 bean.xml 的时候,对象使用 <property name="" ref=""/>
来配置:
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<property name="age" value="18"/>
<!-- ref指定为id="clazz" -->
<property name="clazz" ref="clazz"/>
</bean>
<bean id="clazz" class="com.foooor.hellospring.pojo.Clazz">
<property name="name" value="三年2班"/>
</bean>
2
3
4
5
6
7
8
9
10
11
上面这种方式是最常用的方式了。
方式二:内部bean
还可以使用内部 bean 的方式进行配置:
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="clazz">
<!-- 在bean的内部定义bean,只能在当前bean内部被引用,其他的bean无法引用 -->
<bean name="clazzInner" class="com.foooor.hellospring.pojo.Clazz">
<property name="name" value="三年2班"/>
</bean>
</property>
</bean>
2
3
4
5
6
7
8
9
10
- 在上面的配置中,在一个 bean 的内部定义 bean,这是内部 bean 的配置方式。
方式三:级联属性赋值
还有一种方式,可以使用级联方式赋值,配置如下:
<bean id="clazz" class="com.foooor.hellospring.pojo.Clazz">
<property name="name" value="三年2班"/>
</bean>
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<property name="age" value="18"/>
<!-- 1.首先引用clazz对象 -->
<property name="clazz" ref="clazz"/>
<!-- 2. 为clazz对象的name属性赋值,会覆盖上面clazz中定义的值 -->
<property name="clazz.name" value="三年8班"/>
</bean>
2
3
4
5
6
7
8
9
10
11
12
13
- 首先引用外部bean,然后使用级联属性为外部 bean 的属性赋值,会覆盖 bean 的属性。
一般都用外部 bean 的方式来配置bean,另外两种用的不多。
# 3 空值null
如果要为一个属性赋值为 null,那么在配置 bean 的时候,使用如下形式:
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name">
<!-- 赋值为null -->
<null/>
</property>
</bean>
2
3
4
5
6
- 如果在上面不配置 name 属性,该属性也不会赋值,也会为 null。但是不同的是,配置了属性的时候,会调用 setter,传递参数 null。如果在类中,该属性有默认值,那么就会覆盖默认值。而如果不配置属性,是不会调用 setter 的,那么默认值就不会被覆盖。
需要注意和以下两种方式的区别:
<!-- 赋值为空字符串"" -->
<property name="name" value=""/>
<!-- 赋值为"null"字符串 -->
<property name="name" value=""/>
2
3
4
5
# 4 数组类型
为 Student 类注入数组类型的数据:
public class Student {
private String name;
// 数组类型的数据
private String[] hobbies;
// ...getters and setters
}
2
3
4
5
6
7
8
bean.xml 中配置如下:
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<property name="hobbies">
<!-- 配置数组类型的数据 -->
<array>
<value>篮球</value>
<value>足球</value>
<value>跑步</value>
</array>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
- 使用
<array>
标签进行配置。
如果属性是对象类型的数组,也基本类似,不过 <array>
标签中需要使用 <ref>
标签:
举个栗子:
创建一个 Book.java 类:
public class Book {
private String name;
private String author;
// ...getters and setters
}
2
3
4
5
6
7
然后在 Student 类中添加 Book[] 类型的属性:
public class Student {
private String name;
// 数组类型的数据
private Book[] books;
// ...getters and setters
}
2
3
4
5
6
7
8
9
10
那么在 bean.xml 中配置如下:
<!-- 配置book1对象 -->
<bean id="book1" class="com.foooor.hellospring.pojo.Book">
<property name="name" value="西游记"/>
<property name="author" value="吴承恩"/>
</bean>
<!-- 配置book2对象 -->
<bean id="book2" class="com.foooor.hellospring.pojo.Book">
<property name="name" value="三国演义"/>
<property name="author" value="罗贯中"/>
</bean>
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<!-- 配置学生对象的books属性 -->
<property name="books">
<array>
<ref bean="book1"/>
<ref bean="book2"/>
</array>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 使用
<array>
和<ref>
标签进行配置。
# 5 List类型
List类型的配置和数组的基本一样,将 <array>
标签换成<list>
标签就可以了。
将 Student 的 hobbies
属性修改为 List<String>
:
public class Student {
private String name;
// 数组类型的数据
private List<String> hobbies;
// ...getters and setters
}
2
3
4
5
6
7
8
bean.xml 中配置,使用 <list>
标签即可:
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<property name="hobbies">
<!-- 配置list类型的数据 -->
<list>
<value>篮球</value>
<value>足球</value>
<value>跑步</value>
</list>
</property>
</bean>
2
3
4
5
6
7
8
9
10
11
12
同样,如果是对象类型的 List,和数组类型的配置也是一样的,将 <array>
标签换成<list>
标签即可,这里就不写了。
# 5 Set类型
Set 类型的数据和 List、数组也是类似的,将 <list>
标签换成 <set>
就可以了。
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<property name="hobbies">
<!-- 配置set类型的数据 -->
<set>
<value>篮球</value>
<value>足球</value>
<value>跑步</value>
</set>
</property>
<property name="books">
<!-- 配置set类型的数据 -->
<set>
<ref bean="book1"/>
<ref bean="book2"/>
</set>
</property>
</bean>
<!-- 配置book1对象 -->
<bean id="book1" class="com.foooor.hellospring.pojo.Book">
<property name="name" value="西游记"/>
<property name="author" value="吴承恩"/>
</bean>
<!-- 配置book2对象 -->
<bean id="book2" class="com.foooor.hellospring.pojo.Book">
<property name="name" value="三国演义"/>
<property name="author" value="罗贯中"/>
</bean>
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
- 所以数组、List、Set 三个的配置方式基本一致,只是使用的标签不同而已。
# 6 Map类型
首先在 Student 中创建 Map 类型的属性:
public class Student {
private String name;
// map 类型的数据,成绩
private Map<String, Integer> scores;
// map 类型的数据,书籍
private Map<String, Book> books;
// ...getters and setters
}
2
3
4
5
6
7
8
9
10
11
12
13
在 bean.xml 中配置如下:
<bean id="student" class="com.foooor.hellospring.pojo.Student">
<property name="name" value="张三"/>
<!-- 配置成绩, value 是 int 类型 -->
<property name="scores">
<map>
<entry key="chinese" value="100"/>
<entry key="math" value="90"/>
<entry key="english" value="80"/>
</map>
</property>
<!-- 配置书籍, value 是 Book 对象类型 -->
<property name="books">
<map>
<entry key="xiyouji" value-ref="book1"/>
<entry key="sanguoyanyi" value-ref="book2"/>
</map>
</property>
</bean>
<!-- 配置book1对象 -->
<bean id="book1" class="com.foooor.hellospring.pojo.Book">
<property name="name" value="西游记"/>
<property name="author" value="吴承恩"/>
</bean>
<!-- 配置book2对象 -->
<bean id="book2" class="com.foooor.hellospring.pojo.Book">
<property name="name" value="三国演义"/>
<property name="author" value="罗贯中"/>
</bean>
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
- Map 类型使用
<map>
标签来配置,其中的元素使用<entry>
标签来配置; - Map 中的 value,如果是基本数据类型的使用
value
属性配置,如果是对象类型,那么使用value-ref
来配置。