以配置文件的形式管理java实例的协作方式-- 这种方式叫依赖注入也叫控制反转
传统模式下,当某个java实例(调用者)需要调用另一个java实例(被依赖对象)的方法时,有如下两种方式:
(1)调用者创建被依赖对象的实例,再调用该实例的方法
缺点:new的形式调用被依赖对象的构造器,导致调用者与被依赖对象实现类的硬编码耦合。不利于维护
(2)工厂模式:通过定位到被依赖对象的工厂,通过工厂获取被依赖对象。
优点:1.面向接口编程 2. 不用由调用者创建对象,交由工厂创建被依赖对象的实例
缺点:调用者需要主动通过工厂去获取依赖对象,造成了调用组件与被依赖对象工厂的耦合。
引入Spring框架:无需主动获取被依赖对象,被动接受Spring容器为调用者的成员变量赋值,调用者获取被依赖对象从主动变成被动接受---这就叫控制反转(从调用者的角度看)。
从Spring的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,,为调用者注入它依赖的实例--这就叫依赖注入
小结:依赖注入和控制反转含义差不多(主要看从不同的角度去看)
依赖注入的两种方式:
(1)设值注入
loc容器通过成员变量的setter注入被依赖对象。
示例如下:
Spring推荐面向接口编程
定义两个接口
public interface Person {
//使用斧子
public void useAxe();
}
public interface Axe {
public String chop();
}
Person实现类
public class Chinese implements Person{
private Axe axe;
public void setAxe(Axe axe){
this.axe = axe;
}
@Override
public void useAxe() {
// TODO Auto-generated method stub
System.out.println(axe.chop());
}
}
Axe实现类
public class StoneAxe implements Axe{
@Override
public String chop() {
// TODO Auto-generated method stub
//System.out.println("钢斧砍柴");
return "钢斧砍柴";
}
}
Chinese无需主动调用Axe实例,Axe实例由Spring容器被动注入,这种实现通过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">
<!-- 配置chinese实例 -->
<bean id="chinese" class="com.Chinese">
<property name="axe" ref="stoneAxe"/>
</bean>
<bean id="stoneAxe" class="com.StoneAxe">
</bean></beans>
id:bean的唯一标识,程序通过id来访问bean
class:bean的实现类
Spring会检测<property>的定义,在调用构造器创建bean实例后,调用对应的setter方法为bean成员变量注入值。
测试:
public class beanTest {
public static void main(String[] args) {
//(1) 创建Spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
//(2)获取bean实例
Person p = ctx.getBean("chinese",Person.class);
p.useAxe();
}
}
结果:钢斧砍柴。
假设:后期出现修改,不在想使用钢斧,想使用金刚斧
金刚斧的实现类
public class SteelAxe implements Axe{
@Override
public String chop() {
// TODO Auto-generated method stub
return "金刚斧砍柴真快";
}
}
这时候需要修改的就是配置文件,重新定义Axe实例,修改chinese的传入参数
<!-- 配置chinese实例 -->
<bean id="chinese" class="com.Chinese">
<property name="axe" ref="steelAxe"/>
</bean>
<bean id="steelAxe" class="com.SteelAxe">
</bean></beans>
这时候结果发生改变:
金刚斧砍柴真快。
结论:如此看来,chinese实现类与Axe实现类没有关系,只是借口的耦合,保证了chinese实例与axe实例的松耦合。
Spring根据配置文件或者注解来管理bean的实现类和依赖关系,利用反射创建实例,并注入依赖关系。
(2)构造注入
在构造实例时就完成了依赖关系的初始化,利用构造器来设置依赖关系。
示例:
通过带参数的构造器注入所依赖的bean示例
public class Chinese implements Person{
private Axe axe;
//采用构造的方式注入
public Chinese(Axe axe){
this.axe = axe;
}
@Override
public void useAxe() {
// TODO Auto-generated method stub
System.out.println(axe.chop());
}
}
配置文件:
<!-- 配置chinese实例 -->
<bean id="chinese" class="com.Chinese">
<constructor-arg ref="steelAxe"/>
</bean>
<bean id="steelAxe" class="com.SteelAxe">
</bean></beans>
<constructor-arg>指定构造器参数,通过index属性设定以第几个构造参数传入(0为第一个)
设值注入和构造注入的比较
尽量采用设值注入为主,构造注入为辅的注入策略。
对于依赖关系无需变化的注入------构造注入
依赖关系复杂------------设值注入