欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二、Spring IoC

程序员文章站 2022-07-12 13:09:05
...

一、IoC(Inversion of Control)概述

1. 程序的耦合

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。

一般模块之间可能的连接方式有七种,构成耦合性的七种类型。它们之间的关系为(独立性由强到弱)

  • 非直接耦合(Nondirect Coupling)

如果两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的,这就是非直接耦合。这种耦合的模块独立性最强。

  • 数据耦合(Data Coupling)

如果一个模块访问另一个模块时,彼此之间是通过数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的,则称这种耦合为数据耦合。由于限制了只通过参数表传递数据,按数据耦合开发的程序界面简单、安全可靠。因此,数据耦合是松散的耦合,模块之间的独立性比较强。在软件程序结构中至少必须有这类耦合。

  • 印记耦合(Stamp Coupling)

如果一组模块通过参数表传递记录信息,就是标记耦合。事实上,这组模块共享了这个记录,它是某一数据结构的子结构,而不是简单变量。这要求这些模块都必须清楚该记录的结构,并按结构要求对此记录进行操作。在设计中应尽量避免这种耦合,它使在数据结构上的操作复杂化了。如果采取“信息隐蔽”的方法,把在数据结构上的操作全部集中。

  • 控制耦合(Control Coupling)

如果一个模块通过传送开关、标志、名字等控制信息,明显地控制选择另一模块的功能,就是控制耦合。这种耦合的实质是在单一接口上选择多功能模块中的某项功能。因此,对所控制模块的任何修改,都会影响控制模块。另外,控制耦合也意味着控制模块必须知道所控制模块内部的一些逻辑关系,这些都会降低模块的独立性。

  • 外部耦合(External Coupling)

一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。例如C语言程序中各个模块都访问被说明为extern类型的外部变量。外部耦合引起的问题类似于公共耦合,区别在于在外部耦合中不存在依赖于一个数据结构内部各项的物理安排。

  • 公共耦合(Common Coupling)

若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等。这种耦合会引起下列问题:所有公共耦合模块都与某一个公共数据环境内部各项的物理安排有关,若修改某个数据的大小,将会影响到所有的模块。
无法控制各个模块对公共数据的存取,严重影响软件模块的可靠性和适应性。
公共数据名的使用,明显降低了程序的可读性。公共耦合的复杂程度随耦合模块的个数增加而显著增加。若只是两个模块之间有公共数据环境,则公共耦合有两种情况。若一个模块只是往公共数据环境里传送数据,而另一个模块只是从公共数据环境中取数据,则这种公共耦合叫做松散公共耦合。若两个模块都从公共数据环境中取数据,又都向公共数据环境里送数据,则这种公共耦合叫做紧密公共耦合。只有在模块之间共享的数据很多,且通过参数表传递不方便时,才使用公共耦合。否则,还是使用模块独立性比较高的数据耦合好些。

  • 内容耦合(Content Coupling)

如果发生下列情形,两个模块之间就发生了内容耦合。
一个模块直接访问另一个模块的内部数据;
一个模块不通过正常入口转到另一模块内部;
两个模块有一部分程序代码重叠(只可能出现在汇编语言中);
一个模块有多个入口。
在内容耦合的情形,所访问模块的任何变更,或者用不同的编译器对它再编译,都会造成程序出错

2. 解决程序耦合的思路

  • 使用反射来创建对象,而避免使用new关键字。
  • 通过读取配置文件来获取要创建类的全限定类名。

3. 工厂模式降低程序的耦合

程序获取对象的方式由自己创建对象变为从工厂获取对象,工厂类通过配置文件去获取对象的全限定类名

  1. 工厂类
public class BeanFactory {

    private static Properties prop;

    private static Map<String, Object> beans;

    static {
        prop = new Properties();
        beans = new HashMap<>();
        InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            prop.load(is);
            Enumeration<Object> keys = prop.keys();
            // 保证创建的是单例bean
            while (keys.hasMoreElements()) {
                String name = keys.nextElement().toString();
                String path = prop.getProperty(name);
                Object object = Class.forName(path).newInstance();
                beans.put(name, object);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ExceptionInInitializerError("配置文件初始化失败!");
        }
    }

    public static <T> T getBean(String beanName) {
        return (T) beans.get(beanName);
    }
}
  1. 配置文件
userDao=com.lizza.dao.UserDao
userService=com.lizza.service.impl.UserServiceImpl
  1. 使用工厂类获取对象
public class UserController {

    public static void main(String[] args){
        // new创建的对象增加了代码的耦合度,使用工厂来解耦
        // UserService userService = new UserServiceImpl();
        UserService userService = BeanFactory.getBean("userService");
        userService.saveUser();
    }
}

二、Spring IoC

1. Spring IoC 概述

  • 定义:IoC是指将对象创建的权利交给框架,它包含依赖注入(Dependency Injection)和依赖查找(Dependency Lookup)
  • 作用:降低了程序的耦合

2. 如何使用

  1. 引入依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.4.RELEASE</version>
</dependency>
  1. 创建bean.xml
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userService" class="com.lizza.service.impl.UserServiceImpl"></bean>

    <bean id="userDao" class="com.lizza.dao.UserDao"></bean>

</beans>
  1. 创建容器核心对象
    二、Spring IoC
public class UserController {

    public static void main(String[] args){
        // 1. 创建核心容器对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        // 2. 获取对象
        UserService userService = context.getBean("userService", UserService.class);
        userService.saveUser();
    }
}
  1. 获取bean

3. 深入理解

3.1 ApplicationContext常用的三个实现类

  1. ClassPathXmlApplicationContext:加载类路径下的配置文件来创建容器
  2. FileSystemXmlApplicationContext:加载磁盘任意位置的配置文件来创建容器
  3. AnnotationConfigApplicationContext:读取注解创建容器

3.2 BeanFactory 和 ApplicationContext 两个接口创建对象的时机以及适用场景

二、Spring IoC

接口 创建时机 适用场景
BeanFactory 延迟加载,在需要使用对象的时候才进行创建 多例对象
ApplicationContext 立即加载,容器在初始化的时候立马创建了对象 单例对象