大多数人可能搞混这两个术语。可能是由于在Spring中大量使用了这些概念而导致的混乱,其中使用了Inversion of Control来启用依赖注入。
这篇文章旨在以一种简单的方式来解释这两种说法。
IoC的非正式定义:“ IoC是当你让其他人为你创建对象时。” 因此 ,该对象不是由你的代码编写“ new MyObject”,而是由其他人创建的。此 “其他人” 通常称为IoC容器。
这个简单的解释说明了一些非常重要的想法:
除了Spring之外,还有其他IoC示例,例如 Java Servlet 和 Akka Actors。
让我们进一步研究IoC的定义。IoC不仅仅是对象创建:Spring Context或Servlet容器不仅可以创建对象,而且可以管理对象的整个 生命周期。这包括创建对象,销毁它们以及在对象生命周期的不同阶段调用对象的某些方法。这些方法通常称为 回调。再次注意术语:容器调用的方法是回调,而不是 程序员对自己的代码进行的 直接调用。
前面提到的所有IoC容器都实现某种生命周期: Spring Bean生命周期, Servlet生命周期和 Akka Actor生命周期。
要考虑的另一件事是,尽管程序员放弃了对对象的控制,但是他们仍然需要定义 IoC容器用于创建所述对象的 模板。
例如,在Spring中,类使用@Service 或 @Component进行注释 (还有许多其他注解 ),以表示Spring容器将管理这些类的实例(也可以使用XML配置代替注释)。Spring管理的对象称为Bean。
在Servlet应用程序中,任何实现Servlet 接口的类 都将由Servlet容器管理。
在Akka应用程序中,IoC容器称为 ActorSystem ,托管对象是扩展特征Actor的类的实例, 并通过称为Props的配置对象创建 。
这是到目前为止讨论的想法的简要摘要:
Ioc容器 | 管理对象名称 | 管理对象定义 |
---|---|---|
Spring Container | Bean | Classes defined with annotations/XML configuration |
Servlet Container | Servlet | Classes implementing interface Servlet |
Actor System | Actor | Classes extending trait Actor |
到目前为止,我们已经解释了IoC,还没解释依赖注入(DI)。
依赖注入已成为现代软件工程的基石之一,因为它是允许进行适当测试的基础。简而言之,拥有DI与拥有硬编码的依赖关系相反。
//硬编码
public class MyClass {
private MyDependency myDependency = new MyDependency();
}
//依赖注入
public class MyClass {
private MyDependency myDependency;
public MyClass(MyDependency myDependency){
this.myDependency = myDependency;
}
}
依赖项可以通过几种方式注入,例如构造函数中的参数或通过“ set”方法。
与DI一样重要的是,它的使用也有一个缺点,即:依赖项的管理很不方便。举个个例子:MyClass1依赖于MyClass2,而MyClass3则取决于:
public class MyClass3 {
public void doSomething(){}
}
//MyClass2 依赖 MyClass3
public class MyClass2 {
private MyClass3 myClass3;
public MyClass2(MyClass3 myClass3){
this.myClass3 = myClass3;
}
public void doSomething(){
myClass3.doSomething();
}
}
//MyClass1 依赖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 develope
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的主要作用。使用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();
}
}
我们已经将控制反转和依赖注入作为单独的概念进行了介绍,并说明了在某些情况下如何可以将这两个概念结合起来一起使用
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。