这片文章是对自己学习的总结。
1、什么是依赖注入
简单回忆一下控制反转的概念(IOC)。
开发过程中,我们在一个类中的代码常常会依赖到其他的类对象。比如下面的service类需要用到dao类提供的操作数据库的方法,也就是service依赖dao。没有dao,service的一些方法就没法进行下去。
既然需要这个类对象,那最简单的方式就是直接去new出来。
public class ServiceImpl implements Service {
public void insertData(String data) {
//直接new
Dao dao = new Dao();
dao.insert(data);
}
}
这里ServiceImpl和Dao产生了依赖关系,ServiceImpl依赖Dao。这种情况下,ServiceImpl需要Dao的时候,ServiceImpl是主动去创建一个dao对象(注意是主动创造)。
这种主动创建的行为会带来一些麻烦。因为代码已经写死了,A和B是直接耦合在一起的。如果以后因为业务需求,类B的创建出现了一些问题,比如无法直接new,构造器被隐藏设为私有。等业务人员改完B的代码,重新启动项目后发现A开始报错,又不得不去处理A的逻辑。看到了吧,我们只是想修改B的逻辑,但因为一些依赖关系又不得不去处理很多”原本不应该管的逻辑“。可以想象。这样的依赖关系如果多了的话,那后期维护代码会变得举步维艰。
仔细想想,A和B的依赖中,A只是想要B的服务。A其实不需要关系B的创建过程,只要有个B的对象来提供服务就好。
正是有了这个突破点,控制反转的概念顺势而出,即当类A与类B产生依赖关系时(A需要B),不需要A去主动创建B,而是交给外界创建好B对象,然后通过一些方式把B对象交给A使用。
控制反转的意思是将创建B这个行为的主动权从需要方类A的手中反转到其他人手中。
那外界创建好类B后,将B交给A的行为,就是依赖注入。也就是将A依赖的资源注入到A中。
2、依赖注入的三种方式
2.1 构造器注入
构造器中是可以传参的。以上面的例子为例,我们可以在ServiceImpl中设置一个私有的,类型为Dao的变量。然后ServiceImpl的构造器中传入Dao类型的参数,在构造方法中将传进来的参数赋值给ServiceImpl的私有变量。然后ServiceImpl就可以调用自己的私有变量的方法享受Dao对象提供的服务。
public class ServiceImpl implements Service {
//声明自己的dao对象,等会在构造方法中这个变量赋值
private Dao dao;
public ServiceImpl(Dao dao){
this.dao = dao;
}
public void insertData(String data) {
//这时候dao对象就不是A主动创建的了,是外界创建好,然后在构造方法中注入给ServiceImpl的
dao.insert(data);
}
}
2.2 setter方法注入
与构造器类似,只是注入的时机不是在构造类时,而是构造好类ServiceImpl之后,使用setter方法将Dao对象赋值个ServiceImpl对象的私有成员。
public class ServiceImpl implements Service {
//声明自己的dao对象,等会在构造方法中这个变量赋值
private Dao dao;
public setter(Dao dao) {
this.dao = dao;
}
public void insertData(String data) {
//这时候dao对象就不是A主动创建的了,是外界创建好,然后在构造方法中注入给ServiceImpl的
dao.insert(data);
}
}
2.3 接口注入
接口注入显得很繁琐。我自己感觉也很多此一举。这个方式是定义一个接口,接口中有个方法强制你必须实现一个依赖注入。这样就好像是使用setter方法注入,但是是强制你必须实现setter方法注入。
我们用代码来演示。Service接口中定义了一个inject()方法。这个方法告诉继承者们必须完成dao对象的注入。
public interface Service {
private Dao dao;
/**
* 这个方法用来给dao赋值
*/
public void inject(Dao dao)
}
public class ServiceImpl implements Service {
@Override
public void inject(Dao dao) {
this.dao = dao;
}
}
是不是多此一举?这个方法太繁琐,现在没什么人用了,只是在早起的Avalon的项目中使用。