简介
- 本文分为四部分
- 第一部分:介绍 内聚和耦合 的概念。
- 第二部分:介绍Spring框架中的 IoC控制反转 机制。
- 第三部分:介绍 ApplicationContext 和 BeanFactory 。
- 第四部分:比较 ApplicationContext 和 BeanFactory 。
内聚和耦合
在介绍Spring IoC容器之前,我们先要了解一下软件设计好坏的评判标准:耦合和内聚。
耦合
- 耦合性(Coupling)是对模块间关联程度的度量。
- 耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差。
高耦合
- 手机和充电线是高耦合,为什么呢?
- 打个比方,苹果手机和安卓手机必须使用本机适配的充电线,而且在安卓手机中甚至还分为USB接口和Type-C接口。如果我使用苹果充电接口给安卓手机充电,那么苹果充电线无法正常输电,安卓手机也无法正常充电了。(忽略其他影响)
- 由此可见,手机和充电接口之间的依赖关系紧密,独立性差,所以属于高耦合。
低耦合
- 充电接口和充电接口是低耦合。
- 打个比方,不论是苹果充电接口还是安卓充电接口,都可以使用苹果充电接头,也都可以使用安卓充电接头。(忽略其他影响)
- 由此可见,充电接口和充电接头之间的依赖关系松散,独立性高,所以属于低耦合。
内聚
- 内聚(Cohesion)是一个模块内部各成分间关联程度的度量。
- 内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。
- 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
低内聚
- 职责混乱是低内聚。
- 打个比方,开发人员除了开发项目,还要参与需求分析;测试人员除了测试项目,还要参与开发。这表示每一个内聚模块都没有做好自己分内的事,因为如果每个内聚模块都做好了自己该做的事情,别的模块是不会有插手的余地的。
高内聚
- 各司其职是高内聚。
- 打个比方,软件开发人员完成项目开发,测试人员完成项目测试。这表示每一个内聚模块都做好了自己分内的事情,不需要别的内聚模块来插手,也不需要去别的内裤模块插手。
控制反转 IoC(Inversion of Control)
- 为什么要使用控制反转呢?
- 我们在前面已经了解到“高内聚低耦合”是评判软件设计好坏的标准,以及“内聚”和“耦合”的定义。那么我们学习的 Spring 框架是否能帮助我们更好的设计出“高内聚低耦合”的软件呢?答案显然是肯定的。
- 但是,Spring 框架使得程序“低耦合”的特点更加突出。因为在 Spring 框架中,由于控制反转 IoC(Inversion of Control)的机制,软件之间的依赖关系由 Spring 框架帮我们管理,这样做有效的降低了对象之间的耦合性。
- 控制反转是什么呢?
- 控制反转 IoC(Inversion of Control)就是对象之间的依赖关系由容器来创建,对象之间的关系本来是由我们开发者自己创建和维护的,在我们使用Spring框架后,对象之间的关系由容器来创建和维护,将开发者做的事让容器做。
- 换句话说,就是指new实例工作不再由程序员来做,而是交给Spring容器来做。new实例工作的控制权不再由程序员掌控。
- 我们通过依赖注入,来实现控制反转。
依赖注入 DI(Dependency Injection)
- Spring 最被认同的技术是控制反转的依赖注入,依赖注入是一种设计模式。
- 控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。
- 当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能的独立于其他的 Java 类来增加这些类可重用可能性,当进行单元测试时,可以使它们独立于其他类进行测试。依赖注入有助于将这些类粘合在一起,并且在同一时间让它们保持独立。
- 到底什么是依赖注入?让我 们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类 A 依赖于类 B,表示类 B 将通过 IoC 被注入到类 A 中。
- 依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法。
目录结构
- src
- main
- java.cn.water
- POJO.java(实体类)
- resources
- ApplicationContext
- Beans.xml(Spring配置文件)
- BeanFactory
- Beans.xml(Spring配置文件)
- ApplicationContext
- java.cn.water
- test
- java.cn.water.test
- ApplicationContext
- SpringTest.java(测试类)
- BeanFactory
- SpringTest.java(测试类)
- ApplicationContext
- java.cn.water.test
- main
- pom.xml(Maven配置文件)
Maven配置文件
1 |
|
实体类
POJO.java
1 | package cn.water; |
配置文件
ApplicationContext
1 |
|
BeanFactory
1 |
|
测试类
ApplicationContext
1 | package cn.water.test.ApplicationContext; |
BeanFactory
1 | package cn.water.test.BeanFactory; |
BeanFactory 接口
- 它是最简单的容器,给 DI 提供了基本的支持。
- 它用org.springframework.beans.factory.BeanFactory 接口来定义。(beans)
目录结构
- src
- main
- java.cn.water
- POJO.java(实体类)
- resources
- Beans.xml(Spring配置文件)
- java.cn.water
- test
- java.cn.water.test
- SpringTest.java(测试类)
- java.cn.water.test
- main
实体类
POJO.java
1 | package cn.water; |
配置文件
Beans.xml
- bean 标签:表示一个Bean容器
- id 属性:Bean容器的唯一标识符
- class 属性:POJO类的全类名
- property 标签:表示Bean容器的类成员
- name 属性:Bean容器的类成员的唯一标识符
- value 属性:Bean容器的类成员的值
1 | <bean id="pojo" class="cn.water.POJO"> |
测试类
SpringTest.java
1 |
|
输出结果
1 | BeanFactory:恭喜发财! |
ApplicationContext 接口
- ApplicationContext 是 BeanFactory 的子接口,也被称为 Spring 上下文。
- ApplicationContext 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。
- ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
- 由 org.springframework.context.ApplicationContext 接口定义。
目录结构
- src
- main
- java.cn.water
- POJO.java(实体类)
- resources
- Beans.xml(Spring配置文件)
- java.cn.water
- test
- java.cn.water.test
- SpringTest.java(测试类)
- java.cn.water.test
- main
实体类
POJO.java
1 | package cn.water; |
配置文件
Beans.xml
1 | <bean id="pojo" class="cn.water.POJO"> |
测试类
SpringTest.java
- ClassPathXmlApplicationContext 实现类
- 该容器从 XML 文件中加载已被定义的 bean。
- 在这里,你需要提供 CLASSPATH 环境变量。
- ApplicationContext/Beans.xml
- 在这里,你需要提供 CLASSPATH 环境变量。
- 该容器从 XML 文件中加载已被定义的 bean。
1 |
|
- FileSystemXmlApplicationContext 实现类
- 该容器从 XML 文件中加载已被定义的 bean。
- 在这里,你需要提供给构造器 XML 文件的完整路径。
- C:\\src\\main\\resources\\ApplicationContext\\Bean.xml
- C:/src/main/resources/ApplicationContext/Bean.xml
1
2
3
4
5
6
7
8
9
10
11
public void test02(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new FileSystemXmlApplicationContext("D:\\coding\\IDEASpace\\spring\\spring\\section01_Ioc\\src\\main\\resources\\ApplicationContext\\Beans.xml");
/* 2、获取Bean对象 */
POJO pojo = app.getBean("pojo", POJO.class);
/* 3、调用方法 */
String message = pojo.getMessage();
/* 4、输出 */
System.out.println(message);
}
- 在这里,你需要提供给构造器 XML 文件的完整路径。
- 该容器从 XML 文件中加载已被定义的 bean。
输出结果
1 | ApplicationContext:恭喜发财! |
区别
- ApplicationContext
- 在构建核心容器时,创建对象采用 立即加载 的方式。一读取完配置文件,立马创建配置的对象。
- 由于只在读取配置文件时创建对象,所以 适用于单例模式。
- BeanFactory
- 在构建核心容器时,创建对象采用 延迟加载 的方式。什么时候根据id获取对象了,什么时候创建配置的对象。
- 由于每次获取对象时,都会创建对象,所以 适用于多例模式。
ApplicationContext
实体类
POJO.java
- 加入 静态代码块,用于识别 POJO类 是何时被加载的。
1 | package cn.water; |
配置文件
Beans.xml
1 | <bean id="pojo" class="cn.water.POJO"></bean> |
测试类
SpringTest.java
1 |
|
输出结果
1 | POJO类被加载了!!! |
BeanFactory
实体类
POJO.java
- 加入 静态代码块,用于识别 POJO类 是何时被加载的。
1 | package cn.water; |
Spring配置文件
Beans.xml
1 | <bean id="pojo" class="cn.water.POJO"></bean> |
测试类
SpringTest.java
1 |
|
输出结果
1 | POJO类被加载了!!! |