设计模式之美
王争
前Google工程师,《数据结构与算法之美》专栏作者
立即订阅
20297 人已学习
课程目录
已更新 50 讲 / 共 100 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 (1讲)
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
免费
设计模式学习导读 (3讲)
01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想:面向对象 (11讲)
04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?
05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?
06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?
08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?
09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?
10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则与思想:设计原则 (12讲)
15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?
21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?
22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?
23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?
24 | 实战一(下):如何实现一个遵从设计原则的积分兑换系统?
25 | 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
26 | 实战二(下):如何实现一个支持各种统计规则的性能计数器?
设计原则与思想:规范与重构 (11讲)
27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?
28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?
29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?
30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?
31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)
32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)
33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)
34 | 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题
35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?
37 | 实战二(下):重构ID生成器项目中各函数的异常处理代码
设计原则与思想:总结课 (3讲)
38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点
39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)
40 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(下)
设计模式与范式:创建型 (6讲)
41 | 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?
42 | 单例模式(中):我为什么不推荐使用单例模式?又有何替代方案?
43 | 单例模式(下):如何设计实现一个集群环境下的分布式单例模式?
44 | 工厂模式(上):我为什么说没事不要随便用工厂模式创建对象?
45 | 工厂模式(下):如何设计实现一个Dependency Injection框架?
46 | 建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式
不定期加餐 (3讲)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍推荐
春节特别加餐 | 王争:如何学习《设计模式之美》专栏?
免费
设计模式之美
登录|注册

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

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

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

实际上,DI 容器底层最基本的设计思路就是基于工厂模式的。DI 容器相当于一个大的工厂类,负责在程序启动的时候,根据配置(要创建哪些类对象,每个类对象的创建需要依赖哪些其他类对象)事先创建好对象。当应用程序需要使用某个类对象的时候,直接从容器中获取即可。正是因为它持有一堆对象,所以这个框架才被称为“容器”。
DI 容器相对于我们上节课讲的工厂模式的例子来说,它处理的是更大的对象创建工程。上节课讲的工厂模式中,一个工厂类只负责某个类对象或者某一组相关类对象(继承自同一抽象类或者接口的子类)的创建,而 DI 容器负责的是整个应用中所有类对象的创建。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《设计模式之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(26)

  • 勤劳的明酱
    思考题:
     构造器注入不好解决
     setter注入:根据BenDefinition创建的bean可以是未完成的bean,就是说bean里面的属性可以是没有填充过的,这个时候bean依然能创建成功,之后属性,postConstruct、InitializingBean、init-method完成之后才能算是一个完整的bean,所以即使出现循环依赖也能解决。
    2020-02-14
    1
    5
  • Jeff.Smile
    思考题:
    ①构造器初始化方式,无法解决循环依赖
    ②set注入方式初始化,有两种:
    第一种,创建的是单例对象,可以解决。
    第二种,创建的是原型对象,由于di容器不缓存对象导致无法提前暴露一个创建中的对象,依赖对象就会getbean时创建一个新对象,接着又进去循环依赖创建新对象…依然解决不了。
    2020-02-14
    3
  • javaadu
    关于思考题,对象的依赖关系应该是一个有向无环图(DAG),我倾向于在解析配置文件的时候检测是否存在环,不过这样在大型项目中可能性能不会太好。回头研究下Spring咋做的。
    2020-02-14
    2
  • 此鱼不得水
    Spring解决循环依赖的办法是多级缓存。
    2020-02-14
    2
  • Frank
    什么场景下使用工厂模式判断依据为:当创建对象是一个“大工程”时,一般使用工厂模式来实现,将对象的复杂逻辑封装起来,实现对象的创建和使用分离,使得代码结构更加清晰,类的职责更加单一,也体现了依赖反转原则。“大工程”的判断依据有两个:创建过程涉及复杂的if-else逻辑判断和对象创建需要组装多个其他类对象或者需要复杂的初始化过程。
    Spring的IOC容器就是一个典型的工厂,当我们需要Bean时,只要正确的配置了对象创建需要的规则,他们通过IOC容器提供的接口就可以获取对象,实现了控制反转。
    对于课堂讨论:会出现堆栈溢出StackOverflowError异常。
    2020-02-14
    1
  • 好吃不贵
    createBean先用Topology sort看是否有环,然后再按序创建?
    2020-02-14
    1
  • Geek_3b1096
    终于解答了我对于DI的疑惑
    2020-02-14
    1
  • 安静的boy
    看了这边文章突然对Spring的IOC有了一个清晰认识。
    2020-02-16
  • 辣么大
    循环依赖的意思是A依赖于B,B依赖于A(Bean A → Bean B → Bean A)出现循环依赖,首先应该考虑设计出了问题,应该重新划分类的职责。如果是老的项目,则可以按照其他小伙伴给出的解决方式。最好的解决方案还是看官方文档:链接在这里https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans
    Circular dependencies
    If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

    For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

    One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

    Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).
    2020-02-16
  • 平风造雨
    createBean对refBean的依赖是不是可以理解成强依赖和弱依赖,如果是弱依赖的话,即使出现循环依赖,也可以继续执行被依赖bean的创建,等后续再检查这些不完整的Bean,走初始化逻辑。如果是强依赖,是不是只能抛异常出来?
    2020-02-16
  • cricket1981
    不错,授教了!在老师的基础上改进了下,将bean配置属性class换成了index,通过反射获取到合适的构造函数
    2020-02-16
  • FIGNT
    这个争哥的数据结构与算法之美的递归有答案。对象依赖个数可以限制个大小,递归时如果超过该深度就报错。可能有更好的方式,期待老师的答案
    2020-02-16
  • 桂城老托尼
    感谢分享,尝试回答下课堂讨论
    1. scope = singleton 情况下是不会的,递归方法里如果存在了bean 则会直接返回。 如果是prototype 则需要每次调用都new出一个对象来,可能存在风险,spring 是怎么规避这个问题的,需要研究下代码。
    2. ref 文中的实现目测没有检查 ref 的类型是否和对象的属性类型是否一致。
    2020-02-15
  • chanllenge
    这部分代码可以试着自己实现一遍,然后再看spring源码,应该就很好懂了,争哥讲的好
    2020-02-15
  • Dimple
    意外之喜,还能在这里学到Spring相关的知识。状态越来越好,2020越来越好
    2020-02-15
  • Ken张云忠
    如果我们在配置文件中错误地配置了对象之间的依赖关系,导致存在循环依赖,那 BeansFactory 的 createBean() 函数是否会出现堆栈溢出?又该如何解决这个问题呢?
    会出现堆栈溢出.
    分析:配置了循环依赖后,原型模式和单例模式都会有因为无法将对象构建出来而一直递归调用的问题,最终导致堆栈溢出.
    至于解决方式还需要向学友多多学习,构造器方式初始化应是无法解决的;
    set注入方式中单例模式可以解决,但是原型模式还是会出现循环依赖创建实例的问题,这里也没法做循环依赖检测;
    有的提出控制递归的深度和多级缓存,还没理解怎么处理.
    2020-02-15
  • 唐朝农民
    不好意思我搞错了,这里在递归函数里处理了
    2020-02-14
  • 唐朝农民
    BeansFactory中的46-51行那里 所有的ref 都是prototype的,是不是也应该判断其是否singleton的,如果是的话应该直接从singleObjects中获取呢
    2020-02-14
  • aoe
    原来 DI 容器是一个超级工厂
    2020-02-14
  • webmin
    理论上如果没有进行循环依赖检测,就进行创建会出现堆栈溢出。
    解决方法:在对象创建之前,先根据配置信息,把自己的id以及其ref所指向的引用,递归的找出来放入Hash表,检查是否发生碰撞。
    2020-02-14
收起评论
26
返回
顶部