一、介绍
ioc(控制反转)意味对象不用自己创建需要的对象,相反,它们直接从其他地方获得需要的对象。DI(依赖注入)是一个IoC的具体实现,即在运行时通过不同的注入技术,如setter注入、constructor注入或接口注入来提供对象依赖。由于DI是IoC的具体实现,因此看起来两则貌似一致,但是它们的侧重点不同,IoC侧重对象的创建过程,即对象创建创建的控制权交给了其他地方,而DI侧重依赖的注入方式,即通过不同的技术动态注入依赖。
在spring中,ioc和DI经常会混为一谈,因为,spring文档自己都说Ioc也被称为DI,但是在spring中,Ioc更多的是用来使能DI的。
二、控制反转
ioc可以这么理解,其他人来帮你创建对象,而不是自己手动编码“new MyObject”。这个其他人一般就是IOC容器,ioc容器来帮你创建对象,而ioc容器不只是创建对象,还会管理对象的声明周期,每个不同的生命周期都会被ioc容器调用不同的方法。一些ioc容器,比如spring容器、java Servlets容器和Akka Actors。spring容器不仅负责bean的创建,还控制bean的生命周期。servlets容器也是负责创建servlet和管理他的生命周期。
尽管通过IOC,让出了对象创建的控制权,但是还是需要提供模板来让容器创建对象,如下面所示:
IoC Container | Managed Objects Name | Managed Objects Definition |
---|---|---|
Spring Container | Bean | Classes defined with annotations/XML configuration |
Servlet Container | Servlet | Classes implementing interface Servlet |
Actor System | Actor | Classes extending trait Actor |
三、依赖注入
简单来说,DI和硬编码注入是相对的:
//Hardcoded dependency
public class MyClass {
private MyDependency myDependency = new MyDependency();
}
//Injected dependency
public class MyClass {
private MyDependency myDependency;
public MyClass(MyDependency myDependency){
this.myDependency = myDependency;
}
}
可以看出,可以通过传参给构造函数、setter方法来注入依赖。但是DI有个缺点,就是需要管理依赖和手动注入依赖,即创建依赖对象,然后传参。
下面看一个例子,MyClass1依赖MyClass2,MyClass2依赖MyClass3:
public class MyClass3 {
public void doSomething(){}
}
//MyClass2 depends on MyClass3
public class MyClass2 {
private MyClass3 myClass3;
public MyClass2(MyClass3 myClass3){
this.myClass3 = myClass3;
}
public void doSomething(){
myClass3.doSomething();
}
}
//MyClass1 depends on MyClass2
public class MyClass1 {
private MyClass2 myClass2;
public MyClass1(MyClass2 myClass2){
this.myClass2 = myClass2;
}
public void doSomething(){
myClass2.doSomething();
}
}
public class Main {
public static void main(String[] args) {
//All dependencies need to be managed by the developer
MyClass3 myClass3 = new MyClass3();
MyClass2 myClass2 = new MyClass2(myClass3);
MyClass1 myClass1 = new MyClass1(myClass2);
myClass1.doSomething();
}
}
继续下去,如果MyClass2还依赖MyClass4,那么还需要手动添加新依赖:
public class MyClass2 {
private MyClass3 myClass3;
private MyClass4 myClass4;
public MyClass2(MyClass3 myClass3, MyClass4 myClass4){
this.myClass3 = myClass3;
this.myClass4 = myClass4;
}
public void doSomething(){
myClass3.doSomething();
myClass4.doSomething();
}
}
public class Main {
public static void main(String[] args) {
MyClass4 myClass4 = new MyClass4();
MyClass3 myClass3 = new MyClass3();
MyClass2 myClass2 = new MyClass2(myClass3, myClass4);
MyClass1 myClass1 = new MyClass1(myClass2);
myClass1.doSomething();
}
}
即使这样描述并不是错的,但是现实中的一个程序成百上千个依赖分散在不同的代码区域中,我们需要找到,然后再集中起来创建依赖和管理依赖,如上面的例子似的。很不方便,这就是接下来要讨论的。
四、Ioc和DI配合
依赖太多,依赖的创建和依赖注入都很复杂。但是通过IOC,这些依赖可以被IOC容器来管理、创建。通过使用类似@Autowired的注解,容器可以自动在需要的地方注入依赖,因此程序员不需要关注依赖的创建和注入。
public class MyClass1 {
@Autowired
private MyClass2 myClass2;
public void doSomething(){
myClass2.doSomething();
}
}
public class MyClass2 {
@Autowired
private MyClass3 myClass3;
@Autowired
private MyClass4 myClass4;
public void doSomething(){
myClass3.doSomething();
myClass4.doSomething();
}
}
上面没有了对象的创建过程,这是因为ioc容器被提供模板后,会自动创建依赖,比如spring用<bean>标签定义模板。
参考
http://blog.bytecode.tech/inversion-of-control-vs-dependency-injection/