Spring-IoC概念

来源:互联网 发布:查询树形 mysql 编辑:程序博客网 时间:2024/05/29 14:34

IoC概述

  IoC(Inverse of Controller,控制反转)是spring容器的基础,AOP以及声明式事务等功能都以此为基础。对于刚开始接触Spring的程序员来说,IoC这个重要的概念比较晦涩难懂,在此通过一个例子说明这个概念。

1. 通过实例理解IoC概念

  以《墨攻》电影为例,其中有一个场景,当刘德华所饰演的墨者革离到达梁国都城下时,城上梁国守军问道:“来者何人?”刘德华回答:“墨者革离!”我们不妨通过Java语言为这个“城门叩问”的场景编写剧本,并借此理解IoC的概念,代码如下所示。

/** * 通过使用演员编排剧本 */ public class MoAttack {    public void cityGateAsk() {        // 演员直接侵入剧本        LiuDeHua ldh = new LiuDeHua();        ldh.responseAsk("墨者革离!");    }}

  通过代码我们会发现作为具体角色饰演者的刘德华直接侵入剧本,使剧本和演员直接耦合在一起。一个明智的编剧在剧情创作时应围绕故事的角色进行,而不应考虑角色的具体饰演者,这样才能在剧本投拍时自由的选择任何合适的演员,而非绑定在某一个人身上。通过以上分析,我们需要为该剧本的主人公革离定义一个接口,接口如下所示。

/** * 通过使用角色编排剧本 */ public class MoAttack {    public void cityGateAsk() {        // 引入革离角色接口        GeLi geli = new LiuDeHua();        // 通过接口展开剧情        geli.responseAsk("墨者革离!");    }}

  增加GeLi角色后,该剧本的情节通过角色展开,在拍摄时角色由演员饰演。但是MoAttack却同时依赖于GeLi接口和LiuDeHua类,并没有达到我们所期盼的剧本仅依赖于角色的目的。如何让LiuDeHua和剧本无关而又完成GeLi的具体动作呢?我们可以通过导演负责剧本,角色,饰演者三者的协调。通过引入导演,使得剧本和具体饰演者解耦。
  现在,让我们看看IoC的概念,IoC的字面意思是控制反转,概念包含两方面:
    1. 控制:选择GeLi角色扮演者的控制权
    2. 反转:控制权从《墨攻》剧本中移除,转交给导演的手中
  对于软件而言,某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定,即由Spring容器借由Bean配置进行控制。因为IoC确实不够开门见山,因此业界曾进行广泛的讨论,最终软件界的泰斗级任务Martin Fowler提出了DI(Dependency Injection,依赖注入)概念用来代替IoC,即让调用类对某一接口实现类的依赖关系由第三方注入,以移除调用类对某一接口实现类的依赖。

2. IoC类型

  从注入方法上来看,IoC主要可以划分为三种类型:构造函数注入,属性注入和接口注入。Spring支持构造注入以及属性注入。下面继续使用以上的例子说明三种注入方式的区别。

2.1 构造函数注入

  通过调用类的构造函数,将接口实现类通过构造函数变量传入,代码如下所示。

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

  MoAttack的构造函数不关心具体由谁来饰演革离这个角色,只要传入的饰演者按剧本要求完成相应的表演即可,角色的具体饰演者有导演来安排,代码如下所示。

public class Director {    public void direct() {        // 指定角色的饰演者        GeLi geli = new LiuDeHua();        // 注入具体饰演者到剧本中        MoAttack moAttack = new MoAttack(geli);        moAttack.cityGateAsk();    }}

  导演首先安排刘德华饰演革离,并将刘德华“注入”到《墨攻》剧本中,然后进行“城门叩问”剧情的演出工作。

2.2 属性注入

  属性注入可以有选择的通过setter方法完成调用类所需依赖的注入,更加灵活方便,代码如下所示。

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

  代码中为geli属性提供了一个Setter方法,以便让导演在需要时注入geli的具体饰演者,如下代码所示。

public class Director {    public void direct() {        MoAttack moAttack = new MoAttack();        // 调用属性Setter方法注入        GeLi geli = new LiuDeHua();        moAttack.setGeli(geli);        moAttack.cityGateAsk();    }}

  和通过构造函数注入革离饰演者不同,在实例化MoAttack剧本时,并未指定任何饰演者,而是实例化MoAttack后,在需要革离出场时,才调用setGeli()方法注入饰演者。

2.3 接口注入

  将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。为了采取接口注入的方式,必须先声明一个ActorArrangable接口,接口如下:

public interface ActorArrangable {    void injectGeli()}

  MoAttack通过该接口提供具体的实现,代码如下。

public class MoAttack implements ActorArrangable{    private GeLi geli;    // 实现接口方法    public void injectGeli(GeLi geli) {        this.geli = geli;    }    public void cityGateAsk() {        geli.responseAsk("墨者革离!");    }}

  Director通过ActorArrangable的injectGeli()方法完成饰演者的注入工作,代码如下。

public class Director {    public void direct() {        MoAttack moAttack = new MoAttack();        GeLi geli = new LiuDeHua();        moAttack.injectGeli(geli);        moAttack.cityGateAsk();    }}

  由于通过接口注入需要额外声明一个接口,增加了类的数目,而且效果和属性注入并无本质区别,并不推荐这种注入方式。

3. 通过容器完成依赖属性关系的注入

  虽然MoAttack和LiuDeHua实现了解耦,MoAttack无须关注角色实现类的实例化工作,但这些工作中在代码中依然存在,只是转移到Director类中。现在,通过一个第三方的容器,帮助完成类的初始化与装配工作,让开发者从这些底层实现类的实例化,依赖关系装配等工作中解脱出来,专注于更有意义的业务逻辑开发工作。
  Spring就是这样的一个容器,它通过配置文件或注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入工作。当容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用。

原创粉丝点击