Spring IoC启示录

79 阅读4分钟

Spring 两大特性

面向对象与JavaBean

  • 面向对象编程:定义类、创建类的对象、使用对象、销毁对象。
  • Java 应用程序运行时,存在很多实现各类功能的 Java 对象,这些对象又叫做 JavaBean。

为什么叫 Java Bean?

  • Java 的 logo 是一杯咖啡,JavaBean 是咖啡豆,是咖啡的原料。
  • Java 对象是 Java 面向对象的基础,所以 Java 对象又叫 JavaBean。

image.png

在 Java 应用程序中,如何创建对象、如何管理对象非常重要。

三层架构 JavaWeb 应用程序

系统功能描述

  • 这是一个银行系统。用户可以进行查询余额、存钱、取钱操作。

系统架构描述

  • 采用三层架构:Controller 层、Service 层、Dao 层。
  • Controller 层负责与用户交互,接收用户请求,调用 Service 层对象的方法,返回请求响应。
  • Service 层负责实现业务逻辑,调用 Dao 层来操纵数据库。
  • Dao 层负责与数据库直接交互。

image.png

JavaBean由谁来创建?

  • Controller 层的 Servlet 对象是由 Tomcat 创建的,Service 层和 Dao 层的对象由谁创建呢? 如果我们在依赖这些对象的类中创建

image.png

image.png

  • 这种方式有什么问题?
    • 高层模块依赖底层模块,违反了依赖倒置原则

image.png

什么是依赖倒置原则?

  • 依赖倒置原则,DIP(Dependency Inversion Principle)。DIP原则是指高层模块不应该依赖于底层模块,它们都应该依赖于抽象。面向接口编程是 DIP 的一种实现方式。

image.png

  • 下图不是面向接口编程,BankService 还是直接依赖 BankDao

image.png

image.png

需要把创建类对象的控制权交出去,由第三方来完成对象的创建。

IoC思想:由第三方来创建 JavaBean

image.png

image.png

image.png

  • Servlet 对象的创建不由我们定义的第三方控制,所以还需要在 Servlet 对 bankService 进行手动赋值。之后我们会用 SpringMVC 代替 Servlet,这样一来 Controller 层的对象也由第三方创建

image.png

  • BankService 的创建完全由第三方管理,不需要在类定义中给 bankDao 赋值。

image.png

  • 为了满足 DIP 原则,我们把创建对象的控制权交给了第三方。Java 程序员完成类的定义,第三方获取类定义,通过反射创建对象并完成依赖注入(DI),这种思想叫做 IoC。
  • IoC,Inverse of Control,控制反转。指控制权的转移,将对象的创建和管理交给容器来完成,而不是由程序员来完成。
  • DI,Dependency Injection,依赖注入。指将一个对象所依赖的其他对象通过构造函数、属性或方法参数的方式传递给它,通过注入的方式实现依赖关系的管理,DI 是实现 IoC 的一种手段。
  • Spring 是一个基于 IoC 的框架,换句话说,Spring 帮 Java 程序员完成了对象的创建和管理,让 Java 程序员能更专注于业务功能的实现。

DIP、IoC、DI、Spring 的关系

image.png

  • DIP,Dependency Inversion Principle,依赖倒置原则。指高层模块不应该依赖于底层模块,它们都应该依赖于抽象。
  • IoC, Inverse of Control,控制反转。指控制权的转移,将对象的创建和管理交给容器来完成,而不是由程序员。IoC 是实现 DIP 的一种手段。
  • DI, Dependency Injection,依赖注入。指将一个对象所依赖的其他对象通过构造函数、属性或方法参数的方式传递给它,通过注入的方式实现依赖关系的管理,DI 是实现 IoC 的一种手段。
  • Spring 是一个基于 IoC 的框架,Spring 帮 Java 程序员完成了对象的创建和管理。IoC 是 Spring框架最底层的核心。

IoC解决的问题:单例

现有 bean 创建代码存在的问题:创建了不必要的对象,造成了资源的浪费。

  • 当前对象间依赖关系

image.png

  • 理想的对象间依赖关系

image.png

BankService 和 BankDao 都不需要存在多个实例对象,全局只需要一个实例对象即可。 这种模式叫单例

  • 思想:用一个 map 维护 bean,每次获取 bean 都先尝试从 map 中获取,获取不到再创建。

image.png

以上实现是伪单例的,Spring 真正的单例实现比这复杂。

IoC解决的问题:循环依赖

  • 什么叫循环依赖?A 依赖 B,B 依赖 A,依赖关系形成一个环。

image.png

image.png

  • 执行程序,报 StackOverflowError。

image.png

  • 循环依赖分析

image.png

  • 解决思路:增加二级缓存map,bean 对象创建完成之后就放入二级缓存map,属性注入完成后再放入一级缓存map。

image.png

image.png

image.png

image.png

image.png

image.png

  • 为什么要有 getBeanWithEarlyBean 方法?为了保证 getBeanV2 获取到的都是成品 bean。
  • 以上是循环依赖的简单解法,Spring IoC容器解决循环依赖的机制更复杂一些。