跳到主要内容

IoC详解-IoC概览

  • 所有的类会在 Spring容器 中登记,在系统运行时,把需要用的类主动交给系统,所有类的创建、销毁都由 Spring 来控制,把原来对象的生命周期控制从引用它的对象交给 Spring

  • IoC(Inversion of Control,控制倒转) 的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象,这一点是通过 DI(Dependency Injection,依赖注入)来实现的

  • Spring 的 IoC 容器在实现控制反转和依赖注入过程中的两个阶段:

    • 容器启动阶段
    • Bean 实例化阶段
      两个阶段中IOC容器做的事

容器启动阶段

1. 技术实现方式

  • 作为被注入对象,想要让 IoC 容器为其提供服务,并将所需要的依赖对象送过来,需要通过某种方式通知对方
    被注入对象通知 IoC 容器的方式有三种: 构造方法注入、setter 方法注入、接口注入方式(已经退出历史舞台)
    • 接口注入: 不推荐的原因是因为强制被注入对象实现不必要的接口,带有侵入性
    • 构造方法注入
      • 优点: 对象在构造完成之后就进入就绪状态,可以马上使用
        • 保证依赖不可变:注入对象声明为 final
        • 保证依赖不为空:使用过程中因为注入对象为空导致的异常会在启动阶段直接暴露
      • 缺点: 当依赖对象比较多时,构造方法的参数列表也会比较长,而通过反射构造对象的时候,对相同类型的参数处理会比较困难,维护和使用上也比较麻烦,而且在 Java 中,构造方法无法被继承,无法设置默认值,对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便
    • setter 方法注入: 因为方法可以命名,所以 setter 方法注入在描述性上比构造方法注入好一些,另外 setter 方法可以被继承,允许设置默认值,而且具有良好的 IDE 支持。缺点是 对象无法在构造完成后马上进入就绪状态

2. IoC 容器及 IoC 容器如何获取对象间的依赖关系

  • Spring 中的两种 IoC 容器
    ApplicationContext 是 BeanFactory 的子类,可以看作是更强大的 BeanFactory

    • BeanFactory
      基础类型 IoC 容器,提供完整的 IoC 服务支持,如果没有特殊指定,采用延迟初始化策略(lazy-load),只有当客户端对象需要访问容器中的某个受管对象时,才对该受管对象进行初始化及依赖注入,相对来说容器启动初期速度较快,所需资源比较有限适用于资源有限,功能要求不是很严格的场景

    • ApplicationContext
      ApplicationContext 在 BeanFactory 的基础上构建,是相对比较高级的容器实现,提供了其他高级特性,如事件发布、国际化支持等,ApplicationContext 管理的对象在该类型容器启动后默认全部初始化并绑定完成,因此要求更多的系统资源。因为在启动时就完成所有初始化,因此容器启动时间较长,适用于系统资源充足,并且要求更多功能的场景

      两种容器关系如下:
      两种容器关系

  • IoC容器获取对象间依赖关系的方法

    • 最基本的文本文件
    • 描述性较强的XML文件
    • 编写代码来注册对应信息
    • 注解方式来注册对应信息

    一般使用 XML方式和注解方式

3. 加载配置文件信息

  • 在 BeanFactory容器 中,每一个注入对象对应一个 BeanDefinition 实例对象,该实例对象负责保存注入对象的所有必要信息,包括: class 类型、是否是抽象类、构造方法参数以及其他属性等。
  • BeanDefinitionReader: 专门加载解析配置文件的类,子类 XmlBeanDefinitionReader 负责读取解析 Spring 指定格式的 xml 配置文件,之后将解析的文件内容映射到对应的 BeanDefinition

4. 容器中对象的创建和获取

  • Bean 的注册
    容器创建一个对象的过程,实现 Bean 的注册的接口是 BeanDefinitionRegistry,事实上 BeanFactory 只是一个接口,定义了如何获取容器内对象的方法
    常说的 BeanFactory 容器是这个接口的实现类,在 实现 BeanFactory接口的同时也实现了 BeanDefinitionRegistry 接口,这样才能通过容器注册对象和获取对象,通过 BeanDefinitionRegistry 的 registerBeanDefinition(BeanDefinition beanDefinition) 方法来进行 Bean 的注册
    BeanFactory、BeanDefinitionRegistry以及DefaultListableBeanFactory(一个具体的容器)的关系图:
    BeanFactory、BeanDefinitionRegistry以及DefaultListableBeanFactory(一个具体的容器)的关系图

  • Bean 的注册流程:

    1. 配置该 Bean 的依赖信息,通常配置在 xml 文件中
    2. 通过 XmlBeanDefinitionReader 读取文件内容,将文件内容映射到相应的 BeanDefinition
    3. 通过 BeanFactory 和 BeanDefinitionRegistry 的具体实现类(比如 DefaultListableBeanFactory)实现 Bean 的注册和获取
    public static void main(String[] args)
    {
    //创建一个容器
    DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
    //调用方法实现Bean的注册
    BeanFactory container = (BeanFactory)bindViaCode(beanRegistry);
    //通过容器获取对象
    FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
    }
    public static BeanFactory bindViaCode(BeanDefinitionRegistry registry)
    {
    AbstractBeanDefinition newsProvider = new RootBeanDefinition(FXNewsProvider.class,true);

    AbstractBeanDefinition newsListener = new RootBeanDefinition(DowJonesNewsListener.class,true);

    AbstractBeanDefinition newsPersister = new RootBeanDefinition(DowJonesNewsPersister.class,true);

    // 将bean定义注册到容器中
    registry.registerBeanDefinition("djNewsProvider", newsProvider);
    registry.registerBeanDefinition("djListener", newsListener);
    registry.registerBeanDefinition("djPersister", newsPersister);
    // 指定依赖关系
    // 1. 可以通过构造方法注入方式
    ConstructorArgumentValues argValues = new ConstructorArgumentValues();
    argValues.addIndexedArgumentValue(0, newsListener);
    argValues.addIndexedArgumentValue(1, newsPersister);
    newsProvider.setConstructorArgumentValues(argValues);
    // 2. 或者通过setter方法注入方式
    MutablePropertyValues propertyValues = new MutablePropertyValues();
    propertyValues.addPropertyValue(new ropertyValue("newsListener",newsListener));
    propertyValues.addPropertyValue(new PropertyValue("newPersistener",newsPersister));
    newsProvider.setPropertyValues(propertyValues);
    // 绑定完成
    return (BeanFactory)registry;
    }