浅析Spring之IoC

前言

本篇博客会简单介绍Spring IoC容器的知识,认识控制反转及依赖注入。
欢迎收藏米奇罗的博客

1. IoC概述

IoC ( Inverse of Control,控制反转 )是Spring容器的内核,AOP、声明式实务等功能都在此基础上开花结果,但是IoC的概念却比较晦涩难懂,它涉及代码解耦、设计模式、代码优化等问题的考量,下面通过简单的例子说明这个概念。

1.1 通过实例理解IoC概念

用一个简单的剧本--《墨攻》来理解。
《墨攻》这部电影涉及三个解释,角色墨者革离、饰演者刘德华及剧本--革离的救国行为(暂且称为墨攻)。

public class MoAttack{
  public void cityGateAsk(){
    LiuDeHua ldh = new LiuDeHua;
	ldh.responseAsk("我是墨者革离!");
  }
}

从上面剧本我们可以发现,具体饰演者刘德华直接侵入剧本,使剧本和演员直接耦合在一起。
而一部电影的编剧创作时应该围绕故事的角色进行,而不是围绕角色的饰演者进行。这样在拍摄时才能保证可以*地遴选合适的演员。
此时MoAttack同时依赖GiLi接口和LiuDeHua接口,并没有达到我们的期望剧本仅依赖于角色。那怎么让LiuDeHua和剧本无关而又能完成GeLi的具体动作呢?
这就需要引入导演,导演将LiuDeHua安排在GeLi这个角色上,导负责剧本、角色、演员的协调控制。如图所示:

浅析Spring之IoC

通过引入导演,使剧本和演员解耦,对应软件中,导演就像一台装配器,安排演员的表演的具体角色。

反过来理解IoC容器,IoC的字面意思是控制反转,它包括两个方面:

  • 其一是控制
  • 其二是反转

对应前面例子,"控制"是指选择GeLi角色的控制权,"反转"是指这种控制权从剧本中移除,转交到导演手中。对于软件来说,即某一接口具实现类的选择控制权从调用类中移除,转交给第三方决定,即由Spring容器借由Bean配置来控制。

因为IoC确实不够开门见山,最终软件界泰斗级人物Martin Fowler 提出了DI(Dependency Injection,依赖注入)的概念来代替IoC 。依赖注入显然比控制反转直接明了、易于理解。
我们来看一下这篇权威的文章:Inversion of Control Containers and the Dependency Injectionpattern

2. 控制反转与依赖注入的关系

在一篇博客中看到这种说法:IoC是一种思想,DI是一种设计模式,实现IoC的模式。
我个人认为IoC 不只是一种思想。更多是行为,将主控权交予第三方的行为,实现了程序的主控权的反转。就像上面所讲述的,将角色、剧本、演员的主控权交予导演(第三方)。Martin Fowler认为“控制反转”这个概念太泛了,于是决定将这个模式叫做”依赖注入”(Dependency Injection)。

3. 依赖注入的三种方法

从注入方法上看,IoC主要可以划分为3中种类型:构造函数注入、属性注入和接口注入。Spring支持构造函数注入和属性注入。

3.1 构造函数注入

在构造函数中,通过调用类的构造函数,将接口实现类通过构造函变量传入。

public class MoAttack{
  private GeLi geli;

  //注入革离得具体饰演者
  public MoAttack(GeLi geli){
    this.geli = geli;
  }
  public void cityGateAsk(){
    geli..responseAsk("墨者革离!");
  }
}

MoAttack 的构造函数不关心具体由谁饰演革离这个角色,只需按剧本完成相应的表演即可,角色的具体饰演者将由导演来安排。

public class Directer{
  public void directer(){

    //指定角色的饰演者
    GeLi geli = new LiuDeHua();

    //注入具体饰演者到剧本中
    MoAttack moAttack = new MoAttack(geli);
    moAttack.cityGateAsk();
  }
}

3.2 属性注入

对于JavaBean对象来说,通常会通过setXXX()和getXXX()来访问对应属性。通过setter方法,可以更改相应的对象属性。所以当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。

public class MoAttack{
  private GeLi geli;

  //属性注入的方法
  public MoAttack(GeLi geli){
    this.geli = geli;
  }
  public void cityGateAsk(){
    geli..responseAsk("墨者革离!");
  }
}
public class Directer{
  public void directer(){

    MoAttack moAttack = new MoAttack();

    //调用属性setter方法注入
    GeLi geli = new LiuDeHua();
    moAttack.setGeli(geli);
    moAttack.cityGateAsk();
  }
}

3.3 接口注入

除了前面两种注入技术,还可以在接口中定义需要注入的信息,并通过接口完成注入。首先,我需要定义一个接口,组件的注入将通过这个接口进行。
但是从注入方式的使用来说,接口注入是现在不提倡的一种方式,基本处于”退役”状态,因为它强制被注入对象实现不必要的接口。

4. 通过容器完成依赖关系的注入

除了上述几种方法还有一种方法时通过容器来完成注入。这种方法因为涉及Java的反射知识和类装载器,所以就不详细介绍了。
简单理解容器完成依赖注入,就是一个第三方容器来帮助类的初始化与装配工作,让开发者从这些底层实现类的实例化、依赖关系装配等工作中解脱出来,专注于更有意义的业务逻辑开发工作。

5. 小结

对于初学Spring来说,IoC 是可以帮助我们解耦各业务对象间依赖关系的对象绑定方式,理解Ioc和DI能够更好地帮助我们使用Spring框架。

6. 参考资料