Spring-IoC容器

  • 导读
  • Ioc概述
    • 通过分配工作的实例来理解Ioc的概念
    • IoC类型
      • 构造函数注入
      • 属性注入
    • 通过容器完成依赖关系的注入
  • 涉及的Java知识-Java反射

导读

为了更好地理解Spring的IoC容器,在这里我们通过具体的日常工作中分配工作的示例来模拟IOC的概念。

同时,Spring实现依赖注入的Java底层技术是 Java反射,因此我们也会对Java反射进行介绍。


Ioc概述

Ioc (Inverse of Control 控制反转 )是 Spring容器的内核,AOP、声明式事务等功能都是以此为基础。


通过分配工作的实例来理解Ioc的概念

Spring-IoC容器_第1张图片

举个例子,日常工作(设计、开发、测试、集成等等)

小明直接处理开发工作:

================DoDistributedWork ==========================
package com.xgj.master.ioc;

import com.xgj.master.ioc.distributor.ProjectManager;
import com.xgj.master.ioc.intf.Dealer;
import com.xgj.master.ioc.specific.DealerImpl;
import com.xgj.master.ioc.specific.XiaoMing;

public class DoDistributedWork {

    /**
     * 

     * @Title: doDevelopWork

     * @Description: 小明和具体的工作强耦合


     * @return: void
     */
    public void doDevelopWork(){
        //小明直接侵入开发工作
        XiaoMing xiaoMing = new XiaoMing();
        xiaoMing.doWork();
    }
    ......
}

=========================XiaoMing ==========================

package com.xgj.master.ioc.specific;
public class XiaoMing {

    public void doWork() {
        System.out.println(XiaoMing.class.getSimpleName() + " says that he will do the develop work");
    }
}

让我们来看下此时的类关系图

Spring-IoC容器_第2张图片

可以发现: 开发工作和小明强耦合.

明智的管理者,会从工作分配的角度出发,而不会让工作和具体的处理人员强耦合的 ,那如何让小明和开发工作解耦呢?

同样的开发工作,小强小刚等都可以胜任,而不是绑定到小明一个人身上? 那该如何处理呢?

通过上述分析,我们知道需要为处理人员定义一个接口,任何实现了该接口的实现类都可以处理开发工作。

package com.xgj.master.ioc.intf;

public interface Dealer {
    /**
     * 

     * @Title: doDevelopWork

     * @Description: 接口方法

     * @return: void
     */
    public void doDevelopWork();
}

接下来,只要继承该接口,就可以胜任开发工作

package com.xgj.master.ioc.specific;

import com.xgj.master.ioc.intf.Dealer;

public class DealerImpl   implements Dealer{

    @Override
    public void doDevelopWork() {
        System.out.println(DealerImpl.class.getSimpleName() +" says that he will do the work"); 
    }
}

分配工作:

    /**
     * 
     * @Title: doDevelopWork2
     * @Description: 引入接口,只奥是使具体的执行者和开发工作解耦
     * @return: void
     * 
     */
    public void doDevelopWork2(){
        // 引入接口 Dealer
        Dealer dealer = new DealerImpl();
        // 通过接口处理对应的工作
        dealer.doDevelopWork();

    }

此时的UML:

Spring-IoC容器_第3张图片

此时我们发现:
DoDistrubuteWork 同时依赖 Dealer 和 DealerImpl,需要在DoDistrubuteWork 创建DealerImpl,并没有实现 工作只依赖Dealer的效果。

但是Dealer必须通过具体的实现类才能完成工作,如何让DealerImpl 和 DoDistrubuteWork 无关 同时又能完成Dealer 的具体动作呢?

我们引入 PM,UML关系图如下

Spring-IoC容器_第4张图片

/**
     * 

     * @Title: pmDistributeWork

     * @Description: 引入PM,使 工作和具体的执行者解耦


     * @return: void
     */
    public void pmDistributeWork(){
        // 引入PM
        ProjectManager projectManager = new ProjectManager();
        // PM分配工作
        projectManager.distributeWork();
    }

通过引入PM,使得 工作和 具体的处理人员解耦。PM就像一台装配器,安排具体人员处理具体的工作。

现在我们反过来理解IOC。字面意思:控制反转

结合上面的例子:
- 控制: 选择具体Dealer的控制权
- 反转:指的是这种控制权转移到PM手中。

对于软件来说,即某一接口具体实现类的选择控制权从调用类中移除,转交由第三方决定, 即由Spring容器借由Bean配置来进行控制。

关于IoC的另外一个叫法,Martin Fowler提出了DI(Dependecy Injection 依赖注入),即让调用类对你一个接口实现类的依赖关系由地方(容器或者协作类)注入,以移除调用类对某一个接口实现类的依赖。

很显然, 依赖注入比控制反转更加直接明了,易于理解。


IoC类型

从注入方法上看, IoC分为

  1. 构造函数注入
  2. 属性注入
  3. 接口注入

Spring支持 构造函数注入和属性注入。

构造函数注入

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

package com.xgj.master.ioc.consInj;

import com.xgj.master.ioc.specific.DealerImpl;

public class DoDistributedWork {
    private DealerImpl dealerImpl ;

    // 注入Dealer的实现类
    public DoDistributedWork(DealerImpl dealerImpl){
        this.dealerImpl = dealerImpl;
    }


    public void doSomething(){
        dealerImpl.doDevelopWork();
    }
}

DoDistributedWork 的构造函数不关心由谁来处理工作,只要在构造函数中传入的处理者能够完成指定工作即可, 具体的处理者由PM来安排,如下

package com.xgj.master.ioc.consInj;

import com.xgj.master.ioc.specific.DealerImpl;

public class PM {

    public void distribute() {
        // 指定Dealer的具体人员
        DealerImpl dealerImpl = new DealerImpl();
        // 注入dealerImpl到工作中
        DoDistributedWork distributedWork = new DoDistributedWork(dealerImpl);
        distributedWork.doSomething();

    }

    public static void main(String[] args) {
        PM pm = new PM();
        pm.distribute();
    }

}

属性注入

有时候,并非每个场景都需要DealerImpl,在这种情况使用构造函数注入并不妥当 ,可以考虑使用属性注入。

package com.xgj.master.ioc.properInj;

import com.xgj.master.ioc.intf.Dealer;

public class DoDistributedWork {

    private Dealer dealer ;

    // 属性注入
    public void setDealer(Dealer dealer) {
        this.dealer = dealer;
    }

    public void doSomething(){
        dealer.doDevelopWork();
    }   
}

为Dealer提供一个setter方法,以便PM在需要注入Dealer的具体实现类。

package com.xgj.master.ioc.properInj;

import com.xgj.master.ioc.intf.Dealer;
import com.xgj.master.ioc.specific.DealerImpl;

public class PM {

    public void distribute(){
        Dealer dealer = new DealerImpl();

        DoDistributedWork distributedWork = new DoDistributedWork();
        //通过属性setter方法 注入
        distributedWork.setDealer(dealer);

        distributedWork.doSomething();
    }


    public static void main(String[] args) {
        PM pm = new PM();
        pm.distribute();
    }
}

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

虽然实现了 DoDistributedWork 和 DealerImpl的解耦,但是这些代码仍然存在,只是转移到了PM中而已。

如何将PM这部分也不要呢? 假设有个管理部门,管理部分来选择PM、Dealer等等,那么 每个部分之间就都实现了解耦。我们可以更加专注于也位于逻辑的开发。

Spring就是这样的一个容器,通过配置文件或者注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入工作。

Spring-IoC容器_第5张图片

package com.xgj.master.ioc.springInj;

import com.xgj.master.ioc.intf.Dealer;

public class DealerImpl   implements Dealer{

    @Override
    public void doDevelopWork() {
        System.out.println(DealerImpl.class.getSimpleName() +" says that he will do the work"); 
    }
}
package com.xgj.master.ioc.springInj;

public class DoDistributedWork {

    private DealerImpl dealerImpl ;

    public void setDealerImpl(DealerImpl dealerImpl) {
        this.dealerImpl = dealerImpl;
    }


    public void doSomething(){
        dealerImpl.doDevelopWork();
    }   
}


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    
    <bean id="dealerImpl" class="com.xgj.master.ioc.springInj.DealerImpl" />
    <bean id="doDistributedWork" class="com.xgj.master.ioc.springInj.DoDistributedWork"
        p:dealerImpl-ref="dealerImpl" /> 
beans>

测试类

package com.xgj.master.ioc.springInj;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringInjTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        DoDistributedWork doDistributedWork = (DoDistributedWork) context.getBean("doDistributedWork");
        doDistributedWork.doSomething();
    }
}

Spring-IoC容器_第6张图片


涉及的Java知识-Java反射

Spring为什么会这么简洁,仅仅靠一个配置文件就可以实例化并装配好程序用到的Bean呢?

主要归功于Java类反射功能。

详见 Java-Java反射


你可能感兴趣的:(【Spring-IOC】,Spring-IOC手札)