Spring 两大特性
面向对象与JavaBean
- 面向对象编程:定义类、创建类的对象、使用对象、销毁对象。
- Java 应用程序运行时,存在很多实现各类功能的 Java 对象,这些对象又叫做 JavaBean。
为什么叫 Java Bean?
- Java 的 logo 是一杯咖啡,JavaBean 是咖啡豆,是咖啡的原料。
- Java 对象是 Java 面向对象的基础,所以 Java 对象又叫 JavaBean。
在 Java 应用程序中,如何创建对象、如何管理对象非常重要。
三层架构 JavaWeb 应用程序
系统功能描述
- 这是一个银行系统。用户可以进行查询余额、存钱、取钱操作。
系统架构描述
- 采用三层架构:Controller 层、Service 层、Dao 层。
- Controller 层负责与用户交互,接收用户请求,调用 Service 层对象的方法,返回请求响应。
- Service 层负责实现业务逻辑,调用 Dao 层来操纵数据库。
- Dao 层负责与数据库直接交互。
JavaBean由谁来创建?
- Controller 层的 Servlet 对象是由 Tomcat 创建的,Service 层和 Dao 层的对象由谁创建呢? 如果我们在依赖这些对象的类中创建
- 这种方式有什么问题?
- 高层模块依赖底层模块,违反了
依赖倒置原则。
- 高层模块依赖底层模块,违反了
什么是依赖倒置原则?
- 依赖倒置原则,DIP(Dependency Inversion Principle)。DIP原则是指高层模块不应该依赖于底层模块,它们都应该依赖于抽象。面向接口编程是 DIP 的一种实现方式。
- 下图不是面向接口编程,BankService 还是直接依赖 BankDao
需要把创建类对象的控制权交出去,由第三方来完成对象的创建。
IoC思想:由第三方来创建 JavaBean
- Servlet 对象的创建不由我们定义的第三方控制,所以还需要在 Servlet 对 bankService 进行手动赋值。之后我们会用 SpringMVC 代替 Servlet,这样一来 Controller 层的对象也由第三方创建
- BankService 的创建完全由第三方管理,不需要在类定义中给 bankDao 赋值。
- 为了满足 DIP 原则,我们把创建对象的控制权交给了第三方。Java 程序员完成类的定义,第三方获取类定义,通过反射创建对象并完成依赖注入(DI),这种思想叫做 IoC。
- IoC,Inverse of Control,控制反转。指控制权的转移,将对象的创建和管理交给容器来完成,而不是由程序员来完成。
- DI,Dependency Injection,依赖注入。指将一个对象所依赖的其他对象通过构造函数、属性或方法参数的方式传递给它,通过注入的方式实现依赖关系的管理,DI 是实现 IoC 的一种手段。
- Spring 是一个基于 IoC 的框架,换句话说,Spring 帮 Java 程序员完成了对象的创建和管理,让 Java 程序员能更专注于业务功能的实现。
DIP、IoC、DI、Spring 的关系
- DIP,Dependency Inversion Principle,依赖倒置原则。指高层模块不应该依赖于底层模块,它们都应该依赖于抽象。
- IoC, Inverse of Control,控制反转。指控制权的转移,将对象的创建和管理交给容器来完成,而不是由程序员。IoC 是实现 DIP 的一种手段。
- DI, Dependency Injection,依赖注入。指将一个对象所依赖的其他对象通过构造函数、属性或方法参数的方式传递给它,通过注入的方式实现依赖关系的管理,DI 是实现 IoC 的一种手段。
- Spring 是一个基于 IoC 的框架,Spring 帮 Java 程序员完成了对象的创建和管理。IoC 是 Spring框架最底层的核心。
IoC解决的问题:单例
现有 bean 创建代码存在的问题:创建了不必要的对象,造成了资源的浪费。
- 当前对象间依赖关系
- 理想的对象间依赖关系
BankService 和 BankDao 都不需要存在多个实例对象,全局只需要一个实例对象即可。 这种模式叫单例。
- 思想:用一个 map 维护 bean,每次获取 bean 都先尝试从 map 中获取,获取不到再创建。
以上实现是伪单例的,Spring 真正的单例实现比这复杂。
IoC解决的问题:循环依赖
- 什么叫循环依赖?A 依赖 B,B 依赖 A,依赖关系形成一个环。
- 执行程序,报 StackOverflowError。
- 循环依赖分析
- 解决思路:增加二级缓存map,bean 对象创建完成之后就放入二级缓存map,属性注入完成后再放入一级缓存map。
- 为什么要有 getBeanWithEarlyBean 方法?为了保证 getBeanV2 获取到的都是成品 bean。
- 以上是循环依赖的简单解法,Spring IoC容器解决循环依赖的机制更复杂一些。