依赖注入
- Spring框架的核心功能之一就是通过依赖注入(Dependency Injection)的方式来管理Bean之间的依赖关系。
- 每个基于应用程序的 java 多多少少都会使用几个对象,最后与这些对象一起工作来呈现出终端用户所看到的工作的应用程序。依赖注入有助于把这些类粘合在一起,同时保持他们独立。
全面控制
- 假设你现在正在编写一个文本编辑器(TextEditor)的应用程序,并且想要添加拼写检查(SpellCheck)的功能。那么你就需要在TextEditor类中使用SpellCheck类。
- 标准代码看起来是这样的:
public class TextEditor {
private SpellCheck spellCheck;
public TextEditor() {
spellCheck = new SpellCheck();
}
}
控制反转
- 而在 Spring 框架中,我们需要做的则是创建一个 TextEditor 和 spellCheck 之间的依赖关系。
- 在控制反转的场景中,我们反而会做这样的事情:
public 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
- constructor
- SpellCheck.java(实体类)
- TextEditor.java(实体类)
- User.java(实体类)
- inner
- SpellCheck.java(实体类)
- TextEditor.java(实体类)
- User.java(实体类)
- setter
- SpellCheck.java(实体类)
- TextEditor.java(实体类)
- User.java(实体类)
- resources
- collection
- constructor
- inner
- setter
- test
- java.cn.water.test
- collection
- constructor
- inner
- setter
- pom.xml(Maven配置文件)
Maven配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.water</groupId>
<artifactId>section03_DI</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
实体类
collection
JavaCollection.java
package cn.water.collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class JavaCollection {
private List list;
private Set set;
private Map map;
private Properties properties;
public void setList(List list) {
this.list = list;
}
public void setSet(Set set) {
this.set = set;
}
public void setMap(Map map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
System.out.println(this);
}
@Override
public String toString() {
return "JavaCollection{" +
"list=" + list +
", set=" + set +
", map=" + map +
", properties=" + properties +
'}';
}
}
constructor
SpellCheck.java
package cn.water.constructor;
public class SpellCheck {
public SpellCheck() {
System.out.println("SpellCheck:通过无参构造函数,成功实例化!");
}
}
TextEditor.java
package cn.water.constructor;
public class TextEditor {
private SpellCheck spellCheck;
public TextEditor(SpellCheck spellCheck) {
this.spellCheck = spellCheck;
System.out.println("TextEditor:通过带参构造函数,成功实例化!");
}
}
User.java
package cn.water.constructor;
public class User {
private int id;
private String name;
private float money;
public User(int id, String name, float money) {
this.id = id;
this.name = name;
this.money = money;
System.out.println(this);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
inner
SpellCheck.java
package cn.water.inner;
public class SpellCheck {
}
TextEditor.java
package cn.water.inner;
public class TextEditor {
private SpellCheck spellCheck;
public void setSpellCheck(SpellCheck spellCheck) {
this.spellCheck = spellCheck;
System.out.println("TextEditor:通过Setter(InnerBean),成功实例化!");
}
public TextEditor() {
}
public TextEditor(SpellCheck spellCheck) {
this.spellCheck = spellCheck;
System.out.println("TextEditor:通过带参构造函数(InnerBean),成功实例化!");
}
}
User.java
package cn.water.inner;
public class User {
private int id;
private String name;
private float money;
public User(int id, String name, float money) {
this.id = id;
this.name = name;
this.money = money;
System.out.println(this);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
setter
SpellCheck.java
package cn.water.setter;
public class SpellCheck {
public SpellCheck() {
System.out.println("SpellCheck:通过无参构造函数,成功实例化!");
}
}
TextEditor.java
package cn.water.setter;
public class TextEditor {
private SpellCheck spellCheck;
public void setSpellCheck(SpellCheck spellCheck) {
this.spellCheck = spellCheck;
System.out.println("TextEditor:通过Setter,成功实例化!");
}
}
User.java
package cn.water.setter;
public class User {
private int id;
private String name;
private float money;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setMoney(float money) {
this.money = money;
System.out.println(this);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
配置文件
collection
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="collection" class="cn.water.collection.JavaCollection">
<property name="list">
<list>
<value>A</value>
<value>A</value>
<value>A</value>
</list>
</property>
<property name="set">
<set>
<value>A</value>
<value>B</value>
<value>C</value>
</set>
</property>
<property name="map">
<map>
<entry key="1" value="A"></entry>
<entry key="2" value="A"></entry>
<entry key="3" value="A"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="one">A</prop>
<prop key="two">A</prop>
<prop key="three">A</prop>
</props>
</property>
</bean>
</beans>
constructor
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="editor" class="cn.water.constructor.TextEditor" >
<constructor-arg ref="check"></constructor-arg>
</bean>
<bean id="check" class="cn.water.constructor.SpellCheck"></bean>
<bean id="user01" class="cn.water.constructor.User">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="cat"></constructor-arg>
<constructor-arg name="money" value="999.12"></constructor-arg>
</bean>
<bean id="user02" class="cn.water.constructor.User">
<constructor-arg index="0" value="2"></constructor-arg>
<constructor-arg index="1" value="dog"></constructor-arg>
<constructor-arg index="2" value="1212.12"></constructor-arg>
</bean>
</beans>
inner
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="editor01" class="cn.water.inner.TextEditor" >
<property name="spellCheck">
<bean class="cn.water.inner.SpellCheck"/>
</property>
</bean>
<bean id="editor02" class="cn.water.inner.TextEditor" >
<constructor-arg name="spellCheck">
<bean class="cn.water.inner.SpellCheck"/>
</constructor-arg>
</bean>
</beans>
setter
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="editor01" class="cn.water.setter.TextEditor" >
<property name="spellCheck" ref="check01"></property>
</bean>
<bean id="check01" class="cn.water.setter.SpellCheck"></bean>
<bean id="editor02" class="cn.water.setter.TextEditor" p:spellCheck-ref="check02"></bean>
<bean id="check02" class="cn.water.setter.SpellCheck"></bean>
<bean id="user" class="cn.water.setter.User">
<property name="id" value="1"></property>
<property name="name" value="cat"></property>
<property name="money" value="99.12"></property>
</bean>
</beans>
测试类
collection
package cn.water.test.collection;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void test01(){
ApplicationContext app = new ClassPathXmlApplicationContext("constructor/Beans.xml");
}
}
constructor
package cn.water.test.constructor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void test01(){
ApplicationContext app = new ClassPathXmlApplicationContext("collection/Beans.xml");
}
}
inner
package cn.water.test.inner;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void test01(){
ApplicationContext app = new ClassPathXmlApplicationContext("inner/Beans.xml");
}
}
setter
package cn.water.test.setter;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void test01(){
ApplicationContext app = new ClassPathXmApplicationContext("setter/Beans.xml");
}
}
基于构造函数注入
- 当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。
- 接下来,我们将依然通过 TextEditor 和 spellCheck 的示例来理解 Spring 基于构造函数的依赖注入。本例中,我们首先在XML配置中,对 TextEditor 的构造参数中注入 spellCheck。这样一来,一旦我们实例化 TextEditor ,Spring 就会在 TextEditor 的带参构造函数中传入其参数 spellCheck 。
- 优点:在获取Bean对象时,必须注入构造参数,否则对象无法创建成功。
- 缺点:改变了Bean对象实例化的方式,我们在创建的对象时,用不到的数据,也必须提供。
实体类
TextEditor
- 成员变量(spellCheck)
- 带参构造函数(依赖注入)
package cn.water.Constructor;
public class TextEditor {
private SpellCheck spellCheck;
public TextEditor(SpellCheck spellCheck) {
this.spellCheck = spellCheck;
System.out.println("TextEditor:通过带参构造函数,成功实例化!");
}
}
spellCheck
package cn.water.Constructor;
public class SpellCheck {
public SpellCheck() {
System.out.println("SpellCheck:通过无参构造函数,成功实例化!");
}
}
配置文件
<bean id="editor" class="cn.water.Constructor.TextEditor" >
<constructor-arg ref="check"></constructor-arg>
</bean>
<bean id="check" class="cn.water.Constructor.SpellCheck"></bean>
基本数据类型
User
package cn.water.Constructor;
public class User {
private int id;
private String name;
private float money;
public User(int id, String name, float money) {
this.id = id;
this.name = name;
this.money = money;
System.out.println(this);
}
}
配置文件
<bean id="user" class="cn.water.Constructor.User">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="cat"></constructor-arg>
<constructor-arg name="money" value="999.12"></constructor-arg>
</bean>
<bean id="user" class="cn.water.Constructor.User">
<constructor-arg index="0" value="2"></constructor-arg>
<constructor-arg index="1" value="dog"></constructor-arg>
<constructor-arg index="2" value="1212.12"></constructor-arg>
</bean>
基于设值函数注入
- 当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数Setter,基于设值函数的 DI 就完成了。
- 你应该注意定义在基于构造函数注入和基于设值函数注入中的 Beans.xml 文件的区别。
- 唯一的区别就是在基于构造函数注入中,我们使用的是**〈bean〉标签中的〈constructor-arg〉元素**,而在基于设值函数注入中,我们使用的是**〈bean〉标签中的〈property〉元素**。
- 第二个你需要注意的点是,如果你要把一个引用传递给一个对象,那么你需要使用 标签的 ref 属性,而如果你要直接传递一个值,那么你应该使用 value 属性。
- 优点:Bean对象实例化的方式不变,可以直接使用默认构造函数。
- 缺点:不能保证所有的类成员都被注入了,即使是必须有值的成员。
实体类
TextEditor
- 成员变量(spellCheck)
- 设置函数(依赖注入)
package cn.water.Setter;
public class TextEditor {
private SpellCheck spellCheck;
public void setSpellCheck(SpellCheck spellCheck) {
this.spellCheck = spellCheck;
System.out.println("TextEditor:通过Setter,成功实例化!");
}
}
spellCheck
package cn.water.Constructor;
public class SpellCheck {
public SpellCheck() {
System.out.println("SpellCheck:通过无参构造函数,成功实例化!");
}
}
配置文件
<bean id="editor" class="cn.water.Setter.TextEditor">
<property name="spellCheck" ref="check"></property>
</bean>
<bean id="check" class="cn.water.Setter.SpellCheck"></bean>
基本数据类型
User
package cn.water.Constructor;
public class User {
private int id;
private String name;
private float money;
public User(int id, String name, float money) {
this.id = id;
this.name = name;
this.money = money;
System.out.println(this);
}
}
配置文件
<bean id="user" class="cn.water.Setter.User">
<property name="id" value="1"></property>
<property name="name" value="cat"></property>
<property name="money" value="999.12"></property>
</bean>
p-namespace
标准 XML 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="editor" class="cn.water.Setter.TextEditor">
<property name="spellCheck" ref="check"></property>
</bean>
<bean id="check" class="cn.water.Setter.SpellCheck"></bean>
</beans>
使用 p-namespace 的 XML 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="editor" class="cn.water.setter.TextEditor" p:spellCheck-ref="check"></bean>
<bean id="check" class="cn.water.setter.SpellCheck"></bean>
</beans>
InnerBeans
- 正如你所知道的 Java 内部类是在其他类的范围内被定义的,同理,Inner Beans 是在其他 bean 的范围内定义的 bean。inner beans 不需要写 id属性,因为它无法被其他Bean对象访问。
基于构造函数注入
Beans
<bean id="editor" class="cn.water.Constructor.TextEditor" >
<constructor-arg ref="check"></constructor-arg>
</bean>
<bean id="check" class="cn.water.Constructor.SpellCheck"></bean>
InnerBeans
<bean id="editor" class="cn.water.Inner.TextEditor" >
<constructor-arg name="spellCheck">
<bean class="cn.water.Inner.SpellCheck"/>
</constructor-arg>
</bean>
基于设值函数注入
Beans
<bean id="editor" class="cn.water.Setter.TextEditor">
<property name="spellCheck" ref="check"></property>
</bean>
<bean id="check" class="cn.water.Setter.SpellCheck"></bean>
InnerBeans
<bean id="editor" class="cn.water.Inner.TextEditor" >
<property name="spellCheck">
<bean class="cn.water.Inner.SpellCheck"/>
</property>
</bean>
注入集合
实体类
- 成员变量
- List 集合
- Set 集合
- Map 集合
- property
- 设值构造函数(依赖注入)
package cn.water.collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class JavaCollection {
private List list;
private Set set;
private Map map;
private Properties properties;
public void setList(List list) {
this.list = list;
}
public void setSet(Set set) {
this.set = set;
}
public void setMap(Map map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
System.out.println(this);
}
}
配置文件
- bean标签
- List集合
- Set集合
- Map集合
- property
<bean id="collection" class="cn.water.collection.JavaCollection">
<property name="list">
<list>
<value>A</value>
<value>A</value>
<value>A</value>
</list>
</property>
<property name="set">
<set>
<value>A</value>
<value>B</value>
<value>C</value>
</set>
</property>
<property name="map">
<map>
<entry key="1" value="A"></entry>
<entry key="2" value="A"></entry>
<entry key="3" value="A"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="one">A</prop>
<prop key="two">A</prop>
<prop key="three">A</prop>
</props>
</property>
</bean>
注入 null/空字符
- 如果你需要传递一个空字符串作为值,那么你可以传递它,如下所示:
- 相当于 Java 代码:POJO.setEmail("")
<bean id="..." class="cn.water.POJO">
<property name="email" value=""/>
</bean>
- 如果你需要传递一个 NULL 值,那么你可以传递它,如下所示:
- 相当于 Java 代码:POJO.setEmail(null)
<bean id="..." class="cn.water.POJO">
<property name="email"><null/></property>
</bean>