依赖注入(Dependency Injection, DI)是一种设计模式,也是Spring框架的核心概念之一。其作用是去除Java类之间的依赖关系,实现松耦合,以便于开发测试。为了更好地理解DI,先了解DI要解决的问题。
如果使用一个类,自然的做法是创建一个类的实例:
class Player{
Weapon weapon;
Player(){
// 与 Sword类紧密耦合
this.weapon = new Sword();
}
public void attack() {
weapon.attack();
}
}
这个方法存在耦合太紧的问题,例如,玩家的武器只能是剑Sword
,而不能把Sword
替换成枪Gun
。要把Sword
改为Gun
,所有涉及到的代码都要修改,当然在代码规模小的时候这根本就不是什么问题,但代码规模很大时,就会费时费力了。
依赖注入是一种消除类之间依赖关系的设计模式。例如,A类要依赖B类,A类不再直接创建B类,而是把这种依赖关系配置在外部xml文件(或java config文件)中,然后由Spring容器根据配置信息创建、管理bean类。
示例:
class Player{
Weapon weapon;
// weapon 被注入进来
Player(Weapon weapon){
this.weapon = weapon;
}
public void attack() {
weapon.attack();
}
public void setWeapon(Weapon weapon){
this.weapon = weapon;
}
}
如上所示,Weapon
类的实例并不在代码中创建,而是外部通过构造函数传入,传入类型是父类Weapon
,所以传入的对象类型可以是任何Weapon
子类。
传入哪个子类,可以在外部xml文件(或者java config文件)中配置,Spring容器根据配置信息创建所需子类实例,并注入Player
类中,如下所示:
<bean id="player" class="com.qikegu.demo.Player">
<construct-arg ref="weapon"/>
bean>
<bean id="weapon" class="com.qikegu.demo.Gun">
bean>
上面代码中
ref指向id="weapon"
的bean,传入的武器类型是Gun
,如果想改为Sword
,可以作如下修改:
<bean id="weapon" class="com.qikegu.demo.Sword">
bean>
只需修改这一处配置就可以。
松耦合,并不是不要耦合。A类依赖B类,A类和B类之间存在紧密耦合,如果把依赖关系变为A类依赖B的父类B0类,在A类与B0类的依赖关系下,A类可使用B0类的任意子类,A类与B0类的子类之间的依赖关系是松耦合的。
可以看到依赖注入的技术基础是多态机制与反射机制。