Spring框架 IOC(原理详解)(一)

一、Spring开源框架的简介

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

  spring的基本框架主要包含六大模块:DAO、ORM、AOP、JEE、WEB、CORE
  这里写图片描述
Spring DAO:Spring 提供了对JDBC的操作支持:JdbcTemplate模板工具类。
Spring ORM:Spring 可以与ORM框架整合,例如Spring整合Hibernate框架,其中Spring 还提供HibernateDaoSupport工具类,简化Hibernate的操作
Spring WEB:Spring 提供了对Struts、Springmvc的支持,支持WEB开发。此时同时Spring自身也提供了基于MVC的解决方案
Spring AOP:Spring提供了面向切面编程,可以给某一层提供事物管理,例如在Service层添加事物控制。
Spring JEE:J2EE开发规范的支持,例如EJB
Spring Core:提供IOC容器对象的创建和处理依赖关系

二、Spring下IOC容器

IOC容器:就是具有依赖注入功能的容器,是可以创建对象的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而”控制反转”是指new实例工作不由程序员来做而是交给Spring容器来做。。在Spring中BeanFactory是IOC容器的实际代表者
下面通过实例理解IOC概念

场景:电影墨攻中:当刘德华所饰演的墨者革离到达梁国都城下,城上梁国守军问道:‘城下和人’,刘德华答到:“墨者隔离”;
下面通过一个java类为这个城门对话进行编剧,借此理解IOC概念

代码一:MoAttack:通过演员安排剧本

public class MoAttack{
    public void cityGateAsk(){
        //演员直接入侵剧本
        LiuDeHua  ldh = new LiuDeHua();
        ldh.responseAsk("墨者革离");
    }
}

上面发现,作为具体角色饰演者的刘德华直接侵入剧本中,使剧本和演员直接耦合在一起。

这里写图片描述

但是一个明智的编剧创作时围绕故事的角色进行,而不是角色的具体饰演者,这样就可以自由的选取合适的演员。通过上面分析,我们需要为该剧本主人隔离定义一个接口:在影片开拍时,导演将LiuDeHua安排在GeLi的角色上,导演将剧本、角色、饰演者装配起来
这里写图片描述
通过引入导演,使得剧本和具体饰演者解耦了,具体软件中 导演相当于装配器,安排演员表演具体的角色。

如果此时还是对ioc不理解 下面我摘抄一篇博客博主的通俗理解,感觉说的很好:


简单理解IOC
例子1:
我们从文章中发现,其实ioc所做的一件事情就是把A和B的强耦合关系,变成A依赖于B的接口的关系,但具体A要实现B接口中哪一种B类型,由C来决定,以达到解耦,通俗来讲,我们在家到饭点的时候就会说“我要吃饭”,我这里代表的是A,饭代表的是B的接口,但是具体是要吃什么饭,那就由你的妈妈在决定,你妈妈给你在碗里放了米饭(B),你就要吃米饭,当然,今天你妈妈开心,也可以给你碗里放一个鸡腿,这个决定权在你的妈妈,也就是我们常说的把控制权交给第三方。一次来达到我(A)和米饭(B)的解耦。
例子2:
IoC与DI
首先想说说IoC(Inversion of Control,控制倒转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

  那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。如果你还不明白的话,我决定放弃。

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。关于反射的相关资料请查阅java doc。
 理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。

如果还不明白,放弃java吧!

DI与IOC 的关系
我们可能会经常听到另一个词:DI,这里,简单的做一下讲解:
因为IOC确实不够开门见山,因此业界曾进行了广泛的讨论,最终软件界的泰斗级人物MartinFowIer提出了DI(依注入:Dependency Injection)的概念用以代替loc,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。“依賴注入”这个名词显然比“控制反转”直接明了、易于理解。
所以,我认为IOC和DI描述的是一件事情,只是从不同的角度来描述:
IOC控制反转:说的是创建对象实例的控制权从代码控制剥离到IOC容器控制,实际上就是我们现在说的第三方,侧重于原理。
DI依赖注入:说的是创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。
说到DI,可能就会有出现这样一个问题,既然DI侧重实现,那么他是怎么实现的呢?也就是怎么注入的呢?


IOC的类型
1、构造函数注入
MoAttact:通过构造函数注入革离扮演者

public class MoAttact{
    private GeLi geli;
    //注入革离的具体扮演者
    public MoAttact(GeLi geli){
        this.geli = geli;
    }
    public void cityGateAsk(){
        geli.response("墨者革离");
    }
}

角色的具体扮演由导演来安排

Director: 通过构造函数注入革离扮演者

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

2、属性注入
有时候会发现,革离虽然是主演,但是不是任何时候都需要他的出现,在这种情况下构造函数注入是不妥的,这个时候可以考虑属性注入
Mottack:通过setter方法注入革离扮演者

public class MoAttact{
    private GeLi geli;
    //属性注入
    public void setGeli(GeLi geli){
        this.geli = geli;
    }
    public void cityGateAsk(){
        geli.response("墨者革离");
    }
}

让导演在需要时候注入

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

3、接口注入

将调用类的依赖注入方法抽取到一个接口中,调用实现接口类方法

public interface ActorAraagable{
    void injectGeli(GeLi geli)
}

实现此类

public class MoAttact implement  ActorAraagable{
    private GeLi geli;
    //实现接口注入
    public void injectGeli(GeLi geli){
        this.geli = geli;
    }
    public void cityGateAsk(){
        geli.response("墨者革离");
    }
}

然后通过导演调用实现类

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

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

以上三种注入方式,虽然MoAttack和LiuDeHua实现了解耦,MoAttack无需关注角色实现类的实例化工作,但是这些工作在代码中依然存在,只是转移到了导演Director类中,假设某一制片人想改边局面,在选择某个剧本的时候,希望通过海选或者第三方机构来选择导演,演员,让他们各司其职,这样 导演、演员都实现了解耦。

第三方机构帮助完成类的初始化与装配工作,让开发者从底层实现类的实例化,依赖关系装配等工作中脱离出来,专注于业务的实现。

//实例化类
<bean id="geli" class="LiuDeHua"/>
<bean id="moAttack" class="com.examples.moAttack"
      p:geli-ref="geli">//通过geli-ref建立依赖关系
</bean>

通过new XmlBeanFactory(bean.xml)等方式启动容器,在容器启动时,Spring根据配置文件描述的信息自动化实例bean并实现装配,后面可以直接使用。

猜你喜欢

转载自blog.csdn.net/qq_34288630/article/details/80223612