对 ioc 的理解

【对 ioc 的理解】

IOC 和 DI 先认为是一回事儿,以理解其中的思想为主。

让调用类对某一接口实现类的依赖关系由第三方(容器或者协作类)注入,以移除调用类对某一接口实现类的依赖。

IOC 的目的是为了解耦,具体是怎么解耦的呢?

IOC 具体实现可以总结为两点:

  1. 类的成员变量用接口类型(父类)声明
  2. 类实例交给第三方管理

1. 类的成员变量用接口类型(父类)声明

《Effective Java》 第 5 条:优先考虑依赖注入来引用资源

public class SpellChecker {

    // 首先用 Lexicon 这是个接口或者父类
    private final Lexicon dict;

    // 其次有构造器来 注入 一个实例
    public SpellChecker(Lexicon dict) {
        this.dict = dict;
    }

    public boolean isValid(String word) {...}
    public List<String> suggestions(String type) {...}
}

直接看上面的代码,可能会想,平时写的代码不就是这个样子吗?这有啥,跟依赖注入有啥关系?

这就是依赖注入。很多人已经使用多年,只是不知道它的名字而已。

没有对比就没有伤害,看下下面的代码,就能理解了

public class SpellChecker {

    private final EnglishLexicon dict = new EnglishLexicon();

    public SpellChecker() {}

    public boolean isValid(String word) {...}
    public List<String> suggestions(String type) {...}
}

现在没有人会写这种代码了,大家已经习惯了上面的。

对比下面的代码,依赖注入的例子里,SpellChecker 只需要认识 Lexicon 这个接口,而不需要认识像 EnglishLexicon 或者 ChineseLexicon 这些具体实现类,就实现了 SpellChecker 与具体实现类的解耦,可复用性也更强,当换实现的时候,只需要在构造方法里,填充不同的实现类的实例即可。

public class Main {
    public static void main(String[] args) {
        // SpellChecker englishSpellChecker = new SpellChecker(new EnglishLexicon());
        // 这里理解为更换国家,需要新的实现类,暂不理解为多个实现类共存
        SpellChecker chineseSpellChecker = new SpellChecker(new ChineseLexicon());
    }
}

2. 类实例交给第三方管理

在上面说注入实例的时候,用了这么一段代码。

public class Main {
    public static void main(String[] args) {
        // SpellChecker englishSpellChecker = new SpellChecker(new EnglishLexicon());
        // 这里理解为更换国家,需要新的实现类,暂不理解为多个实现类共存
        SpellChecker chineseSpellChecker = new SpellChecker(new ChineseLexicon());
    }
}

这里其实就隐含了一个第三方(协作类): main 函数,它管理了 new EnglishLexicon()new ChineseLexicon() 这两个实例,这种方式被称为 手动依赖注入

关于网上说的纠结 这还不是自己 new 的么? 这个问题,现在应该有一个答案了吧,这是 手动依赖注入,其中的 手动 就是在自己 new

别急,我知道你想说 Spring 这种框架,也可能还想说 工厂模式 创建 bean 之类的东西。

手动 那就得有 自动 对吧,Spring, Guice 这类 IOC 容器就是 自动依赖注入 要不 Autowired 咋个会有 Auto 呢?

new 这个动作,是可以自己一个一个去 new,远古时期的猿可能也就是这么干的,但慢慢聪明的猿发现,new 这个动作,实在只是个机械重复性动作,枯燥无聊,还业务无关,而计算机最擅长做的事情就是 重复,所以就把这些工作交给了框架来完成了。

那框架里又是怎么完成呢?无非就是 反射 + 配置文件,来实现 new 这个动作,当然说起来简单,做起来还是有各种困难的,什么循环依赖,引用未初始化完 bean 之类的问题也就接踵而至,具体实现细节,暂且不表,这里只是说明,自动 是怎么来的。

依赖注入框架 就是 自动依赖注入 的第三方了,总管实例的生成,存储,注入等等。容器本体是一个或者多个 Map {beanName: bean 实例}

@Component
public class SpellChecker {

    private final Lexicon dict;

    @Autowired
    public SpellChecker(Lexicon dict) {
        this.dict = dict;
    }

    public boolean isValid(String word) {...}
    public List<String> suggestions(String type) {...}
}

@Configuration
public class LexiconConfig {

    @Bean
    public Lexicon lexicon() {
        // 曾经是修改 xml 里 id="lexicon" 的实现类
        // 现在是修改 new 后面的这个类
        return new EnglishLexicon();    
    }
}

上面的代码就是现代使用 Spring 的时候的代码了,熟悉吧,当需要更换字典的时候就 return new ChineseLexicon();

使用配置文件的方式更能体现自动依赖注入的强大,也更有利于理解。

<bean id="lexicon" class="com.demo.lexicon.EnglishLexicon"></bean>

当需要修改实现的时候,只需要新建一个类,把 id 指过去就可以了,使用者甚至无需重新编译代码,只需要编译新增的这个类就可以了。

<bean id="lexicon" class="com.demo.lexicon.ChineseLexicon"></bean>

如果都是本来就有的类,根据业务调整,更换实现类,只需要改了配置,重启应用就可以了。

对于不知道 Spring 通过配置文件是怎么实例化 bean 的,一个简单示例

// 从 xml 中读到 "lexicon" 和 "com.demo.lexicon.ChineseLexicon" 两个字符串

// ioc 本体的一部分
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 简单示例,不是真实实现
singletonObjects.put("lexicon", Class.forName("com.demo.lexicon.ChineseLexicon").newInstance());

// @Autowired 就是在实例化 SpellChecker 的时候从 singletonObjects 中 getBean("lexicon") 来设置 dict 字段的。
// 这里边儿什么万一 lexicon 还没实例化完怎么办之类的问题,就需要自己去研读 Spring 的代码了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lixifun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值