浅谈控制反转(IoC)与依赖注入(DI)在Spring中应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaominpro/article/details/81876274

0.相关概念

IOC Inversion of Control 控制反转
DI Dependency Injection 依赖注入

DI 是实现IOC的一方式

来自wikipedia:

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

1.一个简单的例子

在IoC出现之前我们一般是这么写代码的:

//code 1
public class TextEditor {

    private SpellChecker checker;

    public TextEditor() {
        this.checker = new SpellChecker(); 
    }
}

上面的代码里面出现了依赖关系,存在强代码耦合。如果使用IoC的思想,我们会这样写代码:

//code 2
public class TextEditor {

    private IocSpellChecker checker;

    public TextEditor(IocSpellChecker checker) {
        this.checker = checker;
    }
}

在code 1中,直接在TextEditor类的构造函数内部实例化SpellChecker (this.checker = new SpellChecker();), 这就意味着TextEditor类直接依赖于SpellChecker类。

而在code 2中,我们在TextEditor构造函数参数中创建了其依赖的抽象checker(而不是直接在构造函数内部实例化依赖)。
这就使得我们可以在TextEditor类的外部,自定义实例化依赖,然后将依赖注入到TextEditor内部。如:

SpellChecker sc = new SpellChecker; // 实例化依赖
TextEditor textEditor = new TextEditor(sc); //注入依赖

2. IOC优点 & DI类型

OK.上面只是一个简单的例子。
那么IOC架构有什么优点呢?

  1. 将任务的执行与其实现分离
  2. 使得在不同的实现之间切换更容易
  3. 程序模块化
  4. 通过隔离组件或模拟其依赖关系并允许组件通过协议进行通信来更轻松地测试程序

(在coding中慢慢体会吧TAT)

依赖注入有如下实现方式:
- 基于接口。实现特定接口以供外部容器注入所依赖类型的对象。
- 基于 set 方法。实现特定属性的public set方法,来让外部容器调用传入所依赖类型的对象。
- 基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
- 基于注解。基于Java的注解功能,在私有变量前加“@Autowired”等注解,不需要显式的定义以上三种代码,便可以让外部容器传入对应的对象。该方案相当于定义了public的set方法,但是因为没有真正的set方法,从而不会为了实现依赖注入导致暴露了不该暴露的接口(因为set方法只想让容器访问来注入而并不希望其他依赖此类的对象访问)。

3.Spring IOC

Spring是如何实现IOC的呢?
Spring所做的远比上面例子中的多,spring为我们提供了IOC容器,这个容器所做的就是帮我们控制各种bean对象的生命周期,以及依赖的注入。spring容器会在创建bean时注入其依赖项,而不用我们像上面code 2中的代码那样手动注入。
这里写图片描述

spring实现DI是基于注解的,其中两个重要的注解是:
@Component
@Autowire

4. Spring IOC使用栗子


//AppConfig.java 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;


@Configuration
@ComponentScan
public class AppConfig {
}
//Apple.java
import org.springframework.stereotype.Component;

@Component
public class Apple {
    private String name;
    private float price;


    Apple() {
        this.name = "apple";
        this.price = 5.5F;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
//AppleTest.java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class AppleTest {
    @Autowired
    private Apple apple;

    @Test
    public void testApple() {
        assertNotNull(apple);
        System.out.println(apple.toString()); //输出:Apple{name='apple', price=5.5}
    }
}

上面的栗子中是在Java中显示配置,你也可以在XML中配置(参考:http://www.importnew.com/24731.html)。
上面的三个主要注解:
- @Componet 表明该类为组件类
- @Autowire 自动装配bean
- @ComponetScan 开启组件扫描
关于spring 注解相关使用就不多说,可以参考《Spring 实战》or 网上资料

OK,如上面所见,测试代码中,没有new Apple(),但是在测试函数中apple!= null ,且正常输出
apple.toString(),这就是spring DI的精华所在啊。Spring框架帮我们管理了这些bean。
关于这方面展开的话又可以写篇blog了。以后再说。

上面代码我已经传到github:https://github.com/zhaominso/ioctest

参考:
https://stackoverflow.com/questions/3058/what-is-inversion-of-control
https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html
https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring

猜你喜欢

转载自blog.csdn.net/zhaominpro/article/details/81876274