Spring IoC容器之---通过实例理解什么是IoC

IoC(Inverse of Control 控制反转)是Spring容器的内核,AOP、声明事物等功能在此基础上开花结果。下面通过实例来理解什么是IoC。


一、什么是IoC

下面以电影《心花怒放》的拍摄举个栗子,涉及到的对象有:导演,主角耿浩(GengHao ),剧本(Xhnf),饰演主角耿浩的演员(黄渤)(HuangBo),电影里面有一幕就是耿浩对郝义说:“Cong gin ya hou,eou tong nei en duan ya gue。(从今以后,我同你恩断义绝!)”。经典!动作(SplitUp)。

a、代码 SI-1
public class Xhnf{
    public void SplitUp(){
        //演员直接侵入剧本,演员黄渤直接与剧本关联起来了,剧本与演员直接耦合了
        HuangBo hb = new HuangBo();
        hb.said("Cong gin ya hou,eou tong nei en duan ya gue");
    }
}

这样设计代码肯定是不行的,不应该直接把耿浩要执行的动作(said)绑定到黄渤,剧本应该围绕角色(耿浩)来设计,我们应该为耿浩这个角色定义一个接口,具体由谁来饰演这个角色我们不知道,就不一定是黄渤了,谁饰演耿浩谁就来执行这个said方法。

b、代码 SI-2
public class Xhnf{
    public void SplitUp(){
        //引入耿浩角色接口
        GengHao gh= new HuangBo();
        //通过接口展开剧情
        gh.said("Cong gin ya hou,eou tong nei en duan ya gue");
    }
}

此时,剧本、耿浩、黄渤三者的类图关系如图所示:

这里写图片描述

可以看出,剧本此时同时依赖GengHao 接口和HuangBo类,并没有达到所期望的剧本仅依赖于角色的目的。
通过导演,可以解决这个耦合问题,导演将HuangBo安排在GengHao 的角色上,导演负责剧本、角色、饰演者三者的协调控制。如图所示:

这里写图片描述

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

  • IoC(Inverse of Control)的字面意思是指控制反转。对应上面的例子,”控制”是指选择GengHao角色扮演者的控制权;”反转”是指这种控制权从Xhnf剧本中移除,转交到导演手中。对于软件来说,即某一接口的具体实现类的选择控制权从调用类中移除,转交给第三方决定,即Spring容器借由Bean配置来进行控制。Martin Flowler提出了DI(Dependency Injection,依赖注入)的概念来解释IoC,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。

二、IoC的类型

IoC主要分为:构造函数注入、属性注入、接口注入。Spring支持构造函数注入和属性注入。

1、构造函数注入

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

a、代码SI-3
public class Xhnf{
    private GengHao gh;
    //①注入耿浩的具体饰演者
    public Xhnf(GengHao gh){
        this.gh = gh;
    }
    public void SplitUp(){
        gh.said("Cong gin ya hou,eou tong nei en duan ya gue");
    }
}

Xhnf的构造函数不关心具体由谁来饰演GengHao这个角色,只要在①处传入饰演者即可,角色的具体饰演者由导演来安排,如代码SI-4所示

b、代码SI-4
public class Director{
    public void direct(){
        //①指定角色的饰演者黄渤
        GengHao gh = new HuangBo();
        //②注入具体饰演者到剧本中
        Xhnf xhnf = new Xhnf(gh );
        //③调用方法执行说台词的操作
        xhnf.SplitUp();
    }
}

2、属性注入

会发现一个问题,那就是虽然GengHao是本片的主角,但是并非每个场景都需要GengHao出现,如果采用构造函数注入,难么只要剧本(Xhnf)一开始,就需要创建GengHao这个角色,在这种情况下通过构造函数注入并不妥当。属性注入可以有选择的通过Setter方法完成调用类所需要依赖的注入,更加灵活方便。

a、代码SI-5
public class Xhnf{
    private GengHao gh;
    //①属性注入耿浩的具体饰演者
    public void setGengHao(GengHao gh){
        this.gh = gh;
    }
    public void SplitUp(){
        gh.said("Cong gin ya hou,eou tong nei en duan ya gue");
    }
}
b、代码SI-6
public class Director{
    public void direct(){
        Xhnf xhnf = new Xhnf();
        GengHao gh = new HuangBo();
        //①调用属性Setter方法注入
        xhnf.setGengHao(gh);
        xhnf.SplitUp();
    }
}

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

3、接口注入

将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。.

a、代码SI-7
public interface ActorArrangable{
    void injectGengHao(GengHao gh);
}
public class Xhnf implements ActorArrangable{
    private GengHao gh;
    //①实现接口方法
    public void injectGengHao(GengHao gh){
        this.gh = gh;
    }
    public void SplitUp(){
        gh.said("Cong gin ya hou,eou tong nei en duan ya gue");
    }
}

Director通过ActorArrangable的injectGengHao()方法完成饰演者的注入工作。

b、代码SI-8
public class Director{
    public void direct(){
        Xhnf xhnf = new Xhnf();
        GengHao gh = new HuangBo();
        xhnf.injectGengHao(gh);
        xhnf.SplitUp();
    }
}

接口注入还要额外声明一个接口,而且它的效果和属性注入并无区别,还是更倾向于使用属性注入的方式。


三、通过容器完成依赖关系的注入

虽然剧本(Xhnf )和演员(HuangBo)实现了解耦,Xhnf 无需关注角色实现类的实例化工作,但这些工作在代码中依然存在,只是转移到了Director类中而已。怎么实现剧本,导演,演员,角色,全部解耦呢?
需要一个第三方容器,它来完成类的初始化与装配工作,开发者就无需关注这些底层实现类的实例化、依赖关系装配等工作,专注于业务逻辑的开发。
Spring就是一个这样的容器,通过配置文件或注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入工作。
在Spring配置文件中,只需要简单的配置就可以完成依赖注入,下面截取一个片段:

<bean id="gengHao" class="HuangBo"/>
<bean id="Xhnf" class="com.cn.Xhnf" p:gengHao-ref="gengHao"/><!--通过gengHao-ref建立依赖关系-->

在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用。底层的实现原理要涉及到Java语言本身的类反射功能,下篇就介绍Java反射。

这里写图片描述

猜你喜欢

转载自blog.csdn.net/axela30w/article/details/77853410
今日推荐