Spring框架(2) —— 控制反转(IoC)

简介

  • 本文分为四部分
    • 第一部分:介绍 内聚和耦合 的概念。
    • 第二部分:介绍 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配置文件)
    • test
      • java.cn.water.test
        • ApplicationContext
          • SpringTest.java(测试类)
        • BeanFactory
          • SpringTest.java(测试类)
  • 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配置文件)
    • test
      • java.cn.water.test
        • SpringTest.java(测试类)

实体类

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配置文件)
    • test
      • java.cn.water.test
        • SpringTest.java(测试类)

实体类

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
    @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
    @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类被加载了!!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值