zoukankan      html  css  js  c++  java
  • java基础 IoC介绍及其简单实现

     

    IoC(Inverse of Control 控制反转)是Spring容器的内核,AOP和声明式事务等功能都是基于此技术实现。

    参照实例理解IoC

    参考网址中的刘德华饰演墨者革离的例子,能帮助我们更好的理解IoC的原理,因此此处我们依然使用这个例子进行IoC的学习。

    代码1:通过演员安排剧本

    public class MoAttack {
        public void cityGateAsk() {
            //演员直接侵入剧本
            LiuDeHua liuDeHua = new LiuDeHua();
            liuDeHua.responseAsk("Who are you?");
        }
    }
    
     
    剧本和演员直接耦合

    这里可以看出,演员同剧本耦合度太高,如果演员临时出现什么状况,可能就会对整部电影有影响。因此聪明的编剧,在创作时应围绕剧情和故事,而不是某一个演员,这样在能在投入拍摄的时候自由遴选演员,而非只能绑定在刘德华的身上。因此此处我们应该真对角色革离设定一个接口。

    代码2:引入革离角色

    public class MoAttack {
        public void cityGateAsk() {
            //引入革离角色接口IGeLi
            IGeLi iGeLi = new LiuDeHua();
            
            //调用角色行为
            iGeLi.responseAsk("Who are you?");
        }
    }
    

    在此处引用了革离角色接口,剧情以角色展开,在拍摄时选择刘德华进行拍摄,进行的也是角色行为。此时墨攻、革离、刘德华的关系就如下图

     
    引入革离角色接口后的关系

    此时MoAttach同时依赖于IGeLi接口和LiuDeHua类,并没有达到真正的剧本只依赖于角色的目的。但是实际剧本拍摄中,又离不开演员,因此如何能让LiuDeHua与剧本MoAttach无关,而又能完成角色IGeLi的具体动作呢?当然是具体拍摄时,导演分配LiuDeHua饰演IGeLi角色,导演将MoAttach剧本、IGeLi角色、饰演者联系在一起。

     
    引入导演后,剧本同饰演者解耦

    通过引入导演,实现了剧本同具体表演者解耦。对应到软件设计中,导演就是一个装配器,安排演员表演具体的角色。
    据此我们可以反过来讲解IoC的概念了。IoC字面意思为控制反转,包括两个内容:控制反转。上述的例子中,控制指的是角色革离扮演者的控制权,而反转是将控制权从剧本中移除,转交到导演的手里。

    将某一接口具体实现类的控制权从调用类中移除,转交给第三方决定。在Spring中,Spring框架就是第三方。

    IoC的类型

    从注入方法上划分,可以主要分为三种类型:构造函数注入、属性注入、接口注入。Spring支持构造函数注入和属性输入。下面我们继续以上面的例子来讲述三种实现方法的区别。

    构造函数注入

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

    代码3.1 通过构造函数注入革离实际扮演者

    public class MoAttack {
        private IGeLi geLi;
        //通过构造函数注入革离的实际扮演者
        public MoAttack(IGeLi iGeLi){
            this.geLi = iGeLi;
        }
    
        public void cityGateAsk() {
            this.geLi.responseAsk("Who are you?");
        }
    }
    

    此处的MoAttack不关心革离的具体扮演者是谁,只需要这个扮演者按照剧本表演革离的动作即可。而革离的具体表演者由导演来安排。

    代码3.2 导演通过剧本的构造函数注入实际表演者

    public class Director {
        private String userName = "FengXiaoGang";
        public void directMovie() {
            //声明革离的实际扮演者
            IGeLi geLi = new LiuDeHua();
    
            //通过MoAttack的构造函数注入革离的实际扮演者
            MoAttack moAttack = new MoAttack(geLi);
            moAttack.cityGateAsk();
        }
    }
    

    属性注入

    在剧本中,革离虽然是第一主角,但是并非在每个场景都会出现。使用构造函数注入的隔离角色,会在每个场景都出现,这也是不恰当的。这时就可以考虑使用属性注入,属性注入可以通过对象的setter方法,灵活的在需要的时候进行调用类所需依赖的注入。

    代码3.3 通过setter方法注入实际扮演者

    public class MoAttack {
        private IGeLi geLi;
        //MoAttack类提供setter方法,用于在导演在需要的时候注入革离的实际扮演者
        public void setGeLi(IGeLi geLi) {
            this.geLi = geLi;
        }
    
        public void cityGateAsk() {
            this.geLi.responseAsk("Who are you?");
        }
    }
    

    代码3.4 导演注入革离实际扮演者

    public class Director {
        private String userName = "FengXiaoGang";
        public void directMovie() {
            //声明革离实际扮演者
            IGeLi geLi = new LiuDeHua();
            MoAttack moAttack = new MoAttack();
            //调用setter方法注入革离实际扮演者
            moAttack.setGeLi(geLi);
            moAttack.cityGateAsk();
        }
    }
    

    在剧本开始,没有革离出场的时候,不生成革离的实际扮演者,等到需要的时候,使用setter方法注入革离的实际扮演者,即可使用。由此可以在需要其他角色出场时,提供其他角色的setter方法,导演在出场时调用setter方法注入实际扮演者即可。

    接口注入

    将调用类中需要注入的方法提取到接口中,调用类通过实现接口提供的注入方法。为了实现接口注入的形式,应当先生成一个接口方法:

    代码3.5 声明接口方法

    public interface IActorArrange {
        //声明革离角色的注入方法
        void injectGeLi(IGeLi geLi);
    }
    

    代码3.6 实现接口内的注入方法

    public class MoAttack implements IActorArrange {
        private IGeLi geLi;
    
        public void injectGeLi(IGeLi geLi) {
            this.geLi = geLi;
        }
    
        public void cityGateAsk() {
            this.geLi.responseAsk("Who are you?");
        }
    }
    

    代码3.7 导演调用接口方法注入革离实际扮演者

    public class Director {
        private String userName = "FengXiaoGang";
        public void directMovie() {
            IGeLi geLi = new LiuDeHua();
            MoAttack moAttack = new MoAttack();
    
            moAttack.injectGeLi(geLi);
            moAttack.cityGateAsk();
        }
    }
    

    通过接口注入方法需要额外声明一个接口,增加类的树木,且其实现效果与属性注入没有本质上的区别,因此不提倡采用此方法。

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

    上述代码最终虽然将剧本和实际扮演者解耦,剧本类无需关注角色扮演类的实例化工作,但是这些代码依然存在,只是转移到了导演类中实例化而已。如果制片人想要在改变这一现状,将角色扮演者的筛选工作交给第三方,这样就真正实现了导演、剧本、角色、实际扮演者的真正解耦。
    所谓第三方在程序中就是一个第三方的容器,它帮助完成类的初始化和实例工作,让开发者从这些底层实现类的实例化、依赖关系装配等工作中脱离出来,专注于更有意义的业务逻辑开发工作。Spring提供的就是这样的容器,它通过配置文件或注解描述类和类的依赖关系,自动完成类的初始化和实例工作。

    代码 配置文件片段

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="geli" class="com.sschen.ioclearning.LiuDeHua"></bean>
        <bean id="moAttack" class="com.sschen.ioclearning.MoAttack">
            <property name="geLi" ref="geli"></property>
        </bean>
    </beans>
    

    在导演类中获取配置节点中内容,调用角色进行表演

    代码 导演类获取配置

    public class Director {
        private String userName = "FengXiaoGang";
        public void directMovie() {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Bean.xml");
            MoAttack moAttack = applicationContext.getBean("moAttack", MoAttack.class);
    
            moAttack.cityGateAsk();
        }
    }
    

    如此就真正实现了同角色实际扮演者的解耦,将这部分工作交给Spring配置来实现。在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用。
    那么为什么Spring能够通过仅仅在配置文件中配置的bean节点,就能实例化并且装配好程序所用的bean呢?这就应改归功于Java语言本身的类反射功能。

  • 相关阅读:
    【3】jQuery学习——入门jQuery选择器之基本选择器
    对于转载引发的问题没见过这样强硬的论坛
    SQL2进制问题
    用SQL只获取日期的方法
    C#算法求2进制的问题
    ASP.NET Ajax In Action!读书笔记1
    Fckeditor配置
    MIME types list
    SQL case when then else end运用
    ASP.Net访问母版页(MasterPage)控件、属性、方法及母版页中调用内容页的方法
  • 原文地址:https://www.cnblogs.com/vegetate/p/9997182.html
Copyright © 2011-2022 走看看