设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

45 | 工厂模式(下):如何设计实现一个Dependency Injection框架?

使用反射语法动态加载类、创建对象
BeansFactory类负责根据BeanDefinition创建对象
解析配置文件为BeanDefinition结构
BeanConfigParser接口和XmlBeanConfigParser实现类
ApplicationContext接口和ClassPathXmlApplicationContext实现类
支持简单的配置语法
支持懒加载和配置对象的初始化和销毁方法
区分单例对象和新创建的对象
使用反射机制动态加载类、创建对象
将所有类对象的创建放到一个工厂类中完成
配置文件包含类对象和创建信息
需要通过配置文件告知DI容器要创建哪些对象
解决方法
可能出现堆栈溢出问题
BeansFactory中的createBean()函数是递归函数
应用场景和底层实现原理
应用到工厂模式
DI容器的核心逻辑
核心工厂类设计
配置文件解析
提供执行入口
最小原型设计
对象生命周期管理
对象创建
配置解析
DI容器负责的事情比工厂模式多
DI容器处理更大的对象创建工程
DI容器底层基于工厂模式设计思路
课堂讨论
重点回顾
如何实现一个简单的DI容器?
DI容器的核心功能有哪些?
工厂模式和DI容器有何区别?
如何设计实现一个Dependency Injection框架

该思维导图由 AI 生成,仅供参考

在上一节课我们讲到,当创建对象是一个“大工程”的时候,我们一般会选择使用工厂模式,来封装对象复杂的创建过程,将对象的创建和使用分离,让代码更加清晰。那何为“大工程”呢?上一节课中我们讲了两种情况,一种是创建过程涉及复杂的 if-else 分支判断,另一种是对象创建需要组装多个其他类对象或者需要复杂的初始化过程。
今天,我们再来讲一个创建对象的“大工程”,依赖注入框架,或者叫依赖注入容器(Dependency Injection Container),简称 DI 容器。在今天的讲解中,我会带你一块搞清楚这样几个问题:DI 容器跟我们讲的工厂模式又有何区别和联系?DI 容器的核心功能有哪些,以及如何实现一个简单的 DI 容器?
话不多说,让我们正式开始今天的学习吧!

工厂模式和 DI 容器有何区别?

实际上,DI 容器底层最基本的设计思路就是基于工厂模式的。DI 容器相当于一个大的工厂类,负责在程序启动的时候,根据配置(要创建哪些类对象,每个类对象的创建需要依赖哪些其他类对象)事先创建好对象。当应用程序需要使用某个类对象的时候,直接从容器中获取即可。正是因为它持有一堆对象,所以这个框架才被称为“容器”。
DI 容器相对于我们上节课讲的工厂模式的例子来说,它处理的是更大的对象创建工程。上节课讲的工厂模式中,一个工厂类只负责某个类对象或者某一组相关类对象(继承自同一抽象类或者接口的子类)的创建,而 DI 容器负责的是整个应用中所有类对象的创建。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了依赖注入(DI)容器的设计原理和实现方法。首先对比了工厂模式和DI容器的区别,指出DI容器是一个大的工厂类,负责整个应用中所有类对象的创建,包括配置解析、对象创建和对象生命周期管理。其次详细介绍了DI容器的核心功能,包括配置解析、对象创建和对象生命周期管理。最后提出了实现一个简单的DI容器的方法,包括最小原型设计、提供执行入口和配置文件解析。通过示例代码展示了DI容器的使用方式和执行入口的设计。文章重点讲解了BeansFactory类中的createBean()函数,以及可能出现的循环依赖问题,并提出了解决方案。整体而言,本文对于想要了解DI容器的读者具有很高的参考价值,尤其适合对底层原理感兴趣的读者。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(81)

  • 最新
  • 精选
  • 郑大钱
    “初级工程师在维护代码,高级工程师在设计代码,资深工程师在重构代码” 依赖注入框架好牛逼呀!当手把手教我设计一个框架之后,才破除了我对框架的权威和迷信。 自己最开始做业务也是在原有框架上面修修补补,回过头来看,发现自己非常能忍,即使原有的框架很难用,自己也能坚持用下去。 转念一想,那不是能忍,那是懒。懒得去理解框架的原理,懒得让它更易用。 像豌豆公主一样保持自己的敏感,是持续改进的动力。

    作者回复: 说的好

    2020-11-17
    19
  • 少年锦时
    beanDefinition.isLazyInit() == false 为什么不直接写成!beanDefinition.isLazyInit() 呢

    作者回复: 也可以~

    2020-07-04
  • 沈康
    默默的掏出了《spring源码深度解析》回顾一番 1、构造器循环依赖 构造器注入的循环依赖是无法解决的,只能抛出bean创建异常使容器无法启动 如何判断是循环依赖? 把正在创建的bean放入到一个(正在创建的map)中,如果依赖创建bean在此map中存在,则抛出异常。 2、setter方法循环依赖 ①单例情况可以解决循环依赖,方法是提前暴露一个返回该单例的工厂方法,让依赖对象可以引用到 ②多例不能解决循环依赖,因为多例不需要缓存
    2020-02-18
    7
    163
  • undefined
    把本文的示例补全成了可执行代码: https://github.com/plusmancn/learn-java/tree/master/src/main/java/Exercise/di 顺便纠正一个笔误: BeansFactory 下 createBean 方法中:singletonObjects.contains 应为 singletonObjects. containsKey
    2020-02-23
    2
    64
  • javaadu
    20200218再次复习: 1. 研究了Spring容器中处理循环依赖的知识点:(1)只能处理单例的、setter注入的循环依赖,其他的注入模式无法处理;(2)依赖缓存处理循环依赖,关键思想是,将正在创建中的对象提前暴露一个单例工厂,让其他实例可以引用到 2. 网上一篇比较好的文章:https://juejin.im/post/5d0d8f64f265da1b7b3193ac
    2020-02-19
    2
    38
  • 简单猫
    不要被这些所谓的专业化名词吓到了 什么三级缓存。a依赖b,b依赖c,c依赖a,d依赖a,b,c什么的,你要解决的核心是不要重复创建。那么你就要把已经创建的对象存起来(map,hashmaps什么的) ,然后再次创建的时候先去缓存map中读取,没有才创建。 创建对象流程:1先反射创建类对象 2然后配置类里面的属性 方法(依赖就在这)。 至于你要怎么利用设计模式解耦 分3级缓存 分别存储完全实例化的对象 未设置属性方法类对象 还是对象工厂 那就看如何好用咯
    2020-05-14
    3
    37
  • 此鱼不得水
    Spring解决循环依赖的办法是多级缓存。
    2020-02-14
    23
  • zhengyu.nie
    基本就是Spring源码大体原型了,委托的BeanFactory在Spring源码里是DefaultListableBeanFactory。循环依赖解决是三级缓存,提前暴露还没有初始化结束的bean。检测是Map存一下过程,aba这样顺序判断,有重复(a出现两次)就是环了。 三级缓存源码对应 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton /** * Return the (raw) singleton object registered under the given name. * <p>Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). * @param beanName the name of the bean to look for * @param allowEarlyReference whether early references should be created or not * @return the registered singleton object, or {@code null} if none found */ @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; } /** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    2020-04-24
    15
  • 王先森
    php开发者默默的去瞅laravel的DI容器
    2020-06-16
    11
  • 好吃不贵
    createBean先用Topology sort看是否有环,然后再按序创建?
    2020-02-14
    1
    9
收起评论
显示
设置
留言
81
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部