简介
- 本文分为四部分
- 第一部分:介绍 内聚和耦合 的概念。
- 第二部分:介绍 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配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.water</groupId>
<artifactId>section01_Ioc</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Spring框架 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
实体类
POJO.java
package cn.water;
public class POJO {
/* 静态代码块 */
static{
System.out.println("POJO类被加载了!!!");
}
/* 成员变量 */
private String message;
/* Getter、Setter */
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
配置文件
ApplicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="pojo" class="cn.water.POJO">
<property name="message" value="ApplicationContext:恭喜发财!"/>
</bean>
</beans>
BeanFactory
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="pojo" class="cn.water.POJO">
<property name="message" value="BeanFactory:恭喜发财!"/>
</bean>
</beans>
测试类
ApplicationContext
package cn.water.test.ApplicationContext;
import cn.water.POJO;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* @author Water
* @date 2019/10/23 - 8:38
* @description ApplicationContext接口
* 1、ClassPathXmlApplicationContext实现类
* 2、FileSystemXmlApplicationContext实现类
* 3、立即加载(适用于单例模式)
*/
public class SpringTest {
/** 实现类:ClassPathXmlApplicationContext */
@Test
public void test01(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext/Beans.xml");
/* 2、获取Bean对象 */
POJO pojo = app.getBean("pojo", POJO.class);
/* 3、调用方法 */
String message = pojo.getMessage();
/* 4、输出 */
System.out.println(message);
}
/** 实现类:FileSystemXmlApplicationContext */
@Test
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);
}
/** 立即加载 */
@Test
public void test03(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext/Beans.xml");
}
}
BeanFactory
package cn.water.test.BeanFactory;
import cn.water.POJO;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
/**
* @author Water
* @date 2019/10/23 - 8:39
* @description BeanFactory接口
* 1、XmlBeanFactory实现类
* 2、延迟加载(适用于多例模式)
*/
public class SpringTest {
/** XmlBeanFactory实现类 */
@Test
public void test01(){
/* 1、加载配置文件 */
ClassPathResource resource = new ClassPathResource("BeanFactory/Beans.xml");
/* 2、初始化Bean对象 */
BeanFactory factory = (BeanFactory) new XmlBeanFactory(resource);
/* 3、获取Bean对象 */
POJO pojo = (POJO) factory.getBean("pojo");
/* 4、执行方法 */
String message = pojo.getMessage();
/* 5、输出 */
System.out.println(message);
}
/** 延迟加载 */
@Test
public void test02(){
/* 1、加载配置文件 */
ClassPathResource resource = new ClassPathResource("BeanFactory/Beans.xml");
/* 2、初始化Bean对象 */
BeanFactory factory = (BeanFactory) new XmlBeanFactory(resource);
/* 3、获取Bean对象 */
POJO pojo = (POJO) factory.getBean("pojo"); /* 此时加载 */
}
}
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
package cn.water;
public class POJO {
/* 成员变量 */
private String message;
/* Getter、Setter */
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
配置文件
Beans.xml
- bean 标签:表示一个Bean容器
- id 属性:Bean容器的唯一标识符
- class 属性:POJO类的全类名
- property 标签:表示Bean容器的类成员
- name 属性:Bean容器的类成员的唯一标识符
- value 属性:Bean容器的类成员的值
<bean id="pojo" class="cn.water.POJO">
<property name="message" value="BeanFactory:恭喜发财!"/>
</bean>
测试类
SpringTest.java
@Test
public void test01(){
/* 1、加载配置文件 */
ClassPathResource resource = new ClassPathResource("BeanFactory/Beans.xml");
/* 2、初始化Bean对象 */
BeanFactory factory = (BeanFactory) new XmlBeanFactory(resource);
/* 3、获取Bean对象 */
POJO pojo = (POJO) factory.getBean("pojo");
/* 4、执行方法 */
String message = pojo.getMessage();
/* 5、输出 */
System.out.println(message);
}
输出结果
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
package cn.water;
public class POJO {
/* 成员变量 */
private String message;
/* Getter、Setter */
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
配置文件
Beans.xml
<bean id="pojo" class="cn.water.POJO">
<property name="message" value="ApplicationContext:恭喜发财!"/>
</bean>
测试类
SpringTest.java
- ClassPathXmlApplicationContext 实现类
- 该容器从 XML 文件中加载已被定义的 bean。
- 在这里,你需要提供 CLASSPATH 环境变量。
- ApplicationContext/Beans.xml
- 在这里,你需要提供 CLASSPATH 环境变量。
- 该容器从 XML 文件中加载已被定义的 bean。
@Test
public void test01(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext/Beans.xml");
/* 2、获取Bean对象 */
POJO pojo = app.getBean("pojo", POJO.class);
/* 3、调用方法 */
String message = pojo.getMessage();
/* 4、输出 */
System.out.println(message);
}
- FileSystemXmlApplicationContext 实现类
- 该容器从 XML 文件中加载已被定义的 bean。
- 在这里,你需要提供给构造器 XML 文件的完整路径。
- C:\\src\\main\\resources\\ApplicationContext\\Bean.xml
- C:/src/main/resources/ApplicationContext/Bean.xml
- 在这里,你需要提供给构造器 XML 文件的完整路径。
- 该容器从 XML 文件中加载已被定义的 bean。
@Test
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);
}
输出结果
ApplicationContext:恭喜发财!
区别
- ApplicationContext
- 在构建核心容器时,创建对象采用 立即加载 的方式。一读取完配置文件,立马创建配置的对象。
- 由于只在读取配置文件时创建对象,所以 适用于单例模式。
- BeanFactory
- 在构建核心容器时,创建对象采用 延迟加载 的方式。什么时候根据id获取对象了,什么时候创建配置的对象。
- 由于每次获取对象时,都会创建对象,所以 适用于多例模式。
ApplicationContext
实体类
POJO.java
- 加入 静态代码块,用于识别 POJO类 是何时被加载的。
package cn.water;
public class POJO {
/* 静态代码块 */
static{
System.out.println("POJO类被加载了!!!");
}
/* 成员变量 */
private String message;
/* Getter、Setter */
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
配置文件
Beans.xml
<bean id="pojo" class="cn.water.POJO"></bean>
测试类
SpringTest.java
@Test
public void test03(){
/* 1、加载配置文件,初始化Bean对象 */
ApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext/Beans.xml");
}
输出结果
POJO类被加载了!!!
BeanFactory
实体类
POJO.java
- 加入 静态代码块,用于识别 POJO类 是何时被加载的。
package cn.water;
public class POJO {
/* 静态代码块 */
static{
System.out.println("POJO类被加载了!!!");
}
/* 成员变量 */
private String message;
/* Getter、Setter */
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Spring配置文件
Beans.xml
<bean id="pojo" class="cn.water.POJO"></bean>
测试类
SpringTest.java
@Test
public void test02(){
/* 1、加载配置文件 */
ClassPathResource resource = new ClassPathResource("BeanFactory/Beans.xml");
/* 2、初始化Bean对象 */
BeanFactory factory = (BeanFactory) new XmlBeanFactory(resource);
/* 3、获取Bean对象 */
POJO pojo = (POJO) factory.getBean("pojo"); /* 此时加载 */
}
输出结果
POJO类被加载了!!!