依赖注入
- Spring框架的核心功能之一就是通过依赖注入(Dependency Injection)的方式来管理Bean之间的依赖关系。
- 每个基于应用程序的 java 多多少少都会使用几个对象,最后与这些对象一起工作来呈现出终端用户所看到的工作的应用程序。依赖注入有助于把这些类粘合在一起,同时保持他们独立。
全面控制
- 假设你现在正在编写一个文本编辑器(TextEditor)的应用程序,并且想要添加拼写检查(SpellCheck)的功能。那么你就需要在TextEditor类中使用SpellCheck类。
- 标准代码看起来是这样的:
1
2
3
4
5
6
7
8
9public class TextEditor {
private SpellCheck spellCheck;
public TextEditor() {
spellCheck = new SpellCheck();
}
}
控制反转
- 而在 Spring 框架中,我们需要做的则是创建一个 TextEditor 和 spellCheck 之间的依赖关系。
- 在控制反转的场景中,我们反而会做这样的事情:
1
2
3
4
5
6
7
8public class TextEditor {
private SpellCheck spellCheck;
public TextEditor(SpellCheck spellCheck) {
this.spellCheck = spellCheck;
}
}
结论
- 在 Spring 框架的控制下,TextEditor 不需要担心 spellCheck 的实现。spellCheck 将会独立实现,并且在 TextEditor 实例化的时候将提供给 TextEditor。
- 我们从 TextEditor 中删除了全面控制,并且把它保存到其他地方(即 XML 配置文件),且依赖关系通过类构造函数被注入到 TextEditor 类中。因此,控制流通过依赖注入(DI)已经“反转”,因为你已经有效地委托依赖关系到一些外部系统。
- 对象不查找它的依赖关系,也不知道依赖关系的位置或类,而这一切都由 Spring 框架控制的。
目录结构
- src
- main
- java.cn.water
- collection
- JavaCollection.java(实体类)
- constructor
- SpellCheck.java(实体类)
- TextEditor.java(实体类)
- User.java(实体类)
- inner
- SpellCheck.java(实体类)
- TextEditor.java(实体类)
- User.java(实体类)
- setter
- SpellCheck.java(实体类)
- TextEditor.java(实体类)
- User.java(实体类)
- collection
- resources
- collection
- Beans.xml(Spring配置文件)
- constructor
- Beans.xml(Spring配置文件)
- inner
- Beans.xml(Spring配置文件)
- setter
- Beans.xml(Spring配置文件)
- collection
- java.cn.water
- test
- java.cn.water.test
- collection
- SpringTest.java(测试类)
- constructor
- SpringTest.java(测试类)
- inner
- SpringTest.java(测试类)
- setter
- SpringTest.java(测试类)
- collection
- java.cn.water.test
- main
- pom.xml(Maven配置文件)
Maven配置文件
1 |
|
实体类
collection
JavaCollection.java
1 | package cn.water.collection; |
constructor
SpellCheck.java
1 | package cn.water.constructor; |
TextEditor.java
1 | package cn.water.constructor; |
User.java
1 | package cn.water.constructor; |
inner
SpellCheck.java
1 | package cn.water.inner; |
TextEditor.java
1 | package cn.water.inner; |
User.java
1 | package cn.water.inner; |
setter
SpellCheck.java
1 | package cn.water.setter; |
TextEditor.java
1 | package cn.water.setter; |
User.java
1 | package cn.water.setter; |
配置文件
collection
1 |
|
constructor
1 |
|
inner
1 |
|
setter
1 |
|
测试类
collection
1 | package cn.water.test.collection; |
constructor
1 | package cn.water.test.constructor; |
inner
1 | package cn.water.test.inner; |
setter
1 | package cn.water.test.setter; |
基于构造函数注入
- 当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。
- 接下来,我们将依然通过 TextEditor 和 spellCheck 的示例来理解 Spring 基于构造函数的依赖注入。本例中,我们首先在XML配置中,对 TextEditor 的构造参数中注入 spellCheck。这样一来,一旦我们实例化 TextEditor ,Spring 就会在 TextEditor 的带参构造函数中传入其参数 spellCheck 。
- 优点:在获取Bean对象时,必须注入构造参数,否则对象无法创建成功。
- 缺点:改变了Bean对象实例化的方式,我们在创建的对象时,用不到的数据,也必须提供。
实体类
TextEditor
- 成员变量(spellCheck)
- 带参构造函数(依赖注入)
1 | package cn.water.Constructor; |
spellCheck
- 无参构造函数(依赖注入)
1 | package cn.water.Constructor; |
配置文件
- bean标签
- constructor-arg 标签
1 | <bean id="editor" class="cn.water.Constructor.TextEditor" > |
基本数据类型
User
- 成员变量(基本数据类型)
- 带参构造函数(依赖注入)
1 | package cn.water.Constructor; |
配置文件
- bean标签
- constructor-arg标签
- name属性
- constructor-arg标签
1 | <bean id="user" class="cn.water.Constructor.User"> |
- bean标签
- constructor-arg标签
- index属性
- constructor-arg标签
1 | <bean id="user" class="cn.water.Constructor.User"> |
基于设值函数注入
- 当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数Setter,基于设值函数的 DI 就完成了。
- 你应该注意定义在基于构造函数注入和基于设值函数注入中的 Beans.xml 文件的区别。
- 唯一的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数注入中,我们使用的是〈bean〉标签中的〈property〉元素。
- 第二个你需要注意的点是,如果你要把一个引用传递给一个对象,那么你需要使用 标签的 ref 属性,而如果你要直接传递一个值,那么你应该使用 value 属性。
- 优点:Bean对象实例化的方式不变,可以直接使用默认构造函数。
- 缺点:不能保证所有的类成员都被注入了,即使是必须有值的成员。
实体类
TextEditor
- 成员变量(spellCheck)
- 设置函数(依赖注入)
1 | package cn.water.Setter; |
spellCheck
- 无参构造函数(依赖注入)
1 | package cn.water.Constructor; |
配置文件
- bean标签
- property 标签
1 | <bean id="editor" class="cn.water.Setter.TextEditor"> |
基本数据类型
User
- 成员变量(基本数据类型)
- 设值函数(依赖注入)
1 | package cn.water.Constructor; |
配置文件
- bean标签
- property 标签
- name属性
- property 标签
1 | <bean id="user" class="cn.water.Setter.User"> |
p-namespace
标准 XML 配置文件
1 |
|
使用 p-namespace 的 XML 配置文件
- 导入第四行另外的命名空间
- bean标签
- p:spellCheck-ref属性
1 |
|
InnerBeans
- 正如你所知道的 Java 内部类是在其他类的范围内被定义的,同理,Inner Beans 是在其他 bean 的范围内定义的 bean。inner beans 不需要写 id属性,因为它无法被其他Bean对象访问。
基于构造函数注入
Beans
1 | <!-- 目标类:带参构造函数 --> |
InnerBeans
1 | <!-- 目标类:带参函数 --> |
基于设值函数注入
Beans
1 | <!-- 目标类:Setter --> |
InnerBeans
1 | <!-- 目标类:设值函数 --> |
注入集合
实体类
- 成员变量
- List 集合
- Set 集合
- Map 集合
- property
- 设值构造函数(依赖注入)
1 | package cn.water.collection; |
配置文件
- bean标签
- List集合
- property标签
- list标签
- value标签
- list标签
- property标签
- Set集合
- property标签
- set标签
- value标签
- set标签
- property标签
- Map集合
- property标签
- map标签
- entry标签
- map标签
- property标签
- property
- property标签
- props标签
- prop标签
- props标签
- property标签
- List集合
1 | <!-- 实体类:设置函数 --> |
注入 null/空字符
- 如果你需要传递一个空字符串作为值,那么你可以传递它,如下所示:
- 相当于 Java 代码:POJO.setEmail(“”)
1 | <bean id="..." class="cn.water.POJO"> |
- 如果你需要传递一个 NULL 值,那么你可以传递它,如下所示:
- 相当于 Java 代码:POJO.setEmail(null)
1 | <bean id="..." class="cn.water.POJO"> |