Spring Bean依赖注入
Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。
依赖注入
每个基于应用程序的 java 都有几个对象,这些对象一起工作来呈现出终端用户所看到的工作的应用程序。当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能独立于其他 Java 类来增加这些类重用的可能性,并且在做单元测试时,测试独立于其他类的独立性。依赖注入(或有时称为布线)有助于把这些类粘合在一起,同时保持他们独立。
DI方法主要由两种变体:
序号 | 依赖注入类型&描述 |
---|---|
1 | Constructor-based dependency injection 当容器调用带有多个参数的构造函数类时,实现基于构造函数的 DI,每个代表在其他类中的一个依赖关系。 |
2 | Setter-based dependency injection 基于 setter 方法的 DI 是通过在调用无参数的构造函数或无参数的静态工厂方法实例化 bean 之后容器调用 beans 的 setter 方法来实现的。 |
Spring基于构造函数的依赖
当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。
实例:
- 创建一个名为DITest的Java工程
- 在工程中导入spring库文件。
- 在src下创建名为org.TestDI01的包,在包下创建两个class文件,分别是TestDI01.java和Tteacher.java
- 在src下创建名为Beans.xml的Bean配置文件
下面是TestDI01.java的内容:
package org.TestDI01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDI01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Tteacher teacher = (Tteacher)context.getBean("Tteacher01");
System.out.println(teacher);
}
}
以下是Tteacher.java的内容:
package org.TestDI01;
public class Tteacher {
String name;
int age;
Tteacher(String name,int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "the teacher name is "+name+" , and he is "+age+" , years old.";
}
}
以下是Beans.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.xsd">
<bean id="Tteacher01" class="org.TestDI01.Tteacher" >
<constructor-arg index="0" value="zs"/>
<constructor-arg index="1" value="20" />
</bean>
</beans>
输出结果如下:
如果存在不止一个参数时,当把参数传递给构造函数时,可能会存在歧义。要解决这个问题,那么构造函数的参数在 bean 定义中的顺序就是把这些参数提供给适当的构造函数的顺序就可以了。
当然也可以用type属性来指定参数类型,容器也会匹配适合的简单类型
最后并且也是最好的传递构造函数参数的方式,使用 index 属性来显式的指定构造函数参数的索引。我在自己的实例中就是用的这中方式。
spring基于设置函数的依赖注入
当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数,基于设值函数的 DI 就完成了。
实例:
- 创建一个名为DITest的Java工程
- 在工程中导入spring库文件。
- 在src下创建名为org.TestDI02的包,在包下创建三个class文件,分别是TestDI02.java和Tteacher.java,Tstudent.java
- 在src下创建名为Beans.xml的Bean配置文件
TestDI02.java的内容如下:
package org.TestDI02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDI02 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Tstudent student = (Tstudent)context.getBean("Tstudent02");
student.test();
}
}
Tstudent.java的内容如下:
package org.TestDI02;
public class Tstudent {
private Tteacher teacher;
public void setTeacher(Tteacher teacher) {
this.teacher = teacher;
}
public void test() {
System.out.println("my teacher's name is "+teacher.getName());
}
}
Tstudent.java的内容如下
package org.TestDI02;
public class Tteacher {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Beans.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.xsd">
<bean id="Tteacher01" class="org.TestDI01.Tteacher" >
<constructor-arg type="int" value="20" />
<constructor-arg type="String" value="zs"/>
</bean>
<bean id="Tteacher02" class="org.TestDI02.Tteacher">
<property name="name" value="zs"></property>
</bean>
<bean id="Tstudent02" class="org.TestDI02.Tstudent">
<property name="teacher" ref="Tteacher02"></property>
</bean>
</beans>
需要注意的是定义在基于构造函数注入和基于设值函数注入中的 Beans.xml 文件的区别。唯一的区别就是在基于构造函数注入中,使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,使用的是〈bean〉标签中的〈property〉元素。
第二个需要注意的点是,如果要把一个引用传递给一个对象,那么需要使用 标签的 ref 属性,而如果要直接传递一个值,那么应该使用 value 属性。
输出结果如下:
使用p-namespce实现xml配置
在上面的实例中使用p-namespace实现Beans.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.xsd">
<bean id="Tteacher01" class="org.TestDI01.Tteacher" >
<constructor-arg type="int" value="20" />
<constructor-arg type="String" value="zs"/>
</bean>
<bean id="Tteacher02" class="org.TestDI02.Tteacher"
p:name="zs"/>
<bean id="Tstudent02" class="org.TestDI02.Tstudent" p:teacher-ref="Tteacher02"></bean>
</beans>
spring注入内部Bean
正如java的内部类实在其他类的内部实现的类,同理内部Bean是在其他定义bean的范围内定义的bean
将上述实例中的Tteacher02和Tstudent两个Bean来操作如下:
<?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.xsd">
<bean id="Tteacher01" class="org.TestDI01.Tteacher" >
<constructor-arg type="int" value="20" />
<constructor-arg type="String" value="zs"/>
</bean>
<bean id="Tstudent02" class="org.TestDI02.Tstudent" >
<property name="teacher">
<bean id="Tteacher02" class="org.TestDI02.Tteacher">
<property name="name" value="zs"></property>
</bean>
</property>
</bean>
</beans>
输出结果: