SpringBoot进阶5:IOC容器的设计与初始化流程

SpringBoot进阶5:IOC容器的设计与初始化流程

经验文章nimo972025-05-13 22:43:473A+A-

1 Spring IOC 容器的使用

1.1 IOC 的概念

SpringBoot 框架提供了一套解决方案来简化 Spring 应用的开发,不过 SpringBoot 框架在内部还是基于 Spring 框架来实现对 Java bean 对象的管理的。

具体为基于 Spring 的 IOC 容器来完成 Java bean 对象的创建和对象间的依赖注入,所以称为 IOC 容器。IOC 的英文全称为 Inverse Of Control,意思是控制反转,即对象的创建与管理工作不在应用自身来维护,而是交给 Spring 框架来完成。所以在讲解 SpringBoot 框架的启动实现之前,在本节先介绍 Spring IOC 容器的相关概念与设计。

1.2 IOC 容器的使用

Spring IOC 容器体现在 Spring 框架的源码中就是 Application 接口,Application 接口的体系结构比较复杂,不过根据应用中所使用的 Spring 框架的配置方式的不同,即根据是基于 xml 文件配置还是 Java 类配置,包含两个常见的实现类,分别为基于 xml 文件的配置对应的 ClassPathXmlApplicationContext 和基于 Java 类对象配置的 AnnotationConfigApplicationContext。

如下代码演示基于 Application 接口的实现类 AnnotationConfigApplicationContext 来完成 Java 对象的创建和依赖注入。

拓展知识:Spring 框架一般用在 Java Web 项目中,不过根据 Spring 的 IOC 容器的定义,Spring 框架本身是与 Java Web 没有直接关系的,即可以单独使用 Spring 框架来对普通 Java 项目的 Java 对象的创建和依赖进行管理。

1、Java 对象依赖关系:对象 A 包含一个类型为 ObjectB 的属性,在 A 对象的 showB 方法中打印属性对象 B 的 name 属性:类型 ObjectA 定义:包含一个类型 ObjectB 的对象属性

@Component
public class ObjectA {

    @Autowired
    private ObjectB objectB;

    public ObjectB getObjectB() {
        return objectB;
    }

    public void setObjectB(ObjectB objectB) {
        this.objectB = objectB;
    }
}

类型 ObjectB 定义:包含一个类型为 String 的成员属性 name

@Component
public class ObjectB {
    private String name = "B";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

2、在一个 Java 配置类中配置 Java 类的组件自动扫描,从而实现 A 对象与 B 对象的创建与依赖注入。如下配置类 ApplicationConfig 是一个空类,主要是通过 @Configuration 注解来表明这是一个配置类,通过 @ComponentScan 注解来启用类对象的自动扫描注入功能,关于这两个注解的更多知识在后面小节详细分析。

@Configuration
@ComponentScan
public class ApplicationConfig {

}

3、通过
AnnotationConfigApplicationContext 类来创建 Spring IOC 容器,并从 IOC 容器中获取 A 对象和调用 A 对象的 showB 方法。如下可以看到打印
了 B 对象的 name,所以实现了 Java 对象的创建与依赖注入:

public class BootApplication {

    public static void main(String[] args) {
        // IOC容器对象
        AnnotationConfigApplicationContext iocContainer = new AnnotationConfigApplicationContext();
        iocContainer.register(ApplicationConfig.class);
        // 创建IOC容器与初始化IOC容器
        iocContainer.refresh();

        // 验证IOC容器对Java对象的创建与依赖注入
        ObjectA objectA = iocContainer.getBean(ObjectA.class);
        System.out.println("ObjectB's name is " + objectA.getObjectB().getName());
    }
}

打印结果如下:ObjectB's name is B

1.3 SpringBoot 框架的 IOC 容器实现

SpringBoot 框架在内部创建 Spring 的 IOC 容器的流程与以上代码基于 AnnotationConfigApplicationContext 类来创建 IOC 容器类似。不同之处是 SpringBoot 框架对 AnnotationConfigApplicationContext 类进行了进一步拓展与实现,在内部嵌套了 Servlet 引擎的实现,如 Tomcat 或 Jetty 等功能组件的嵌入,从而实现了 SpringBoot 应用以 jar 包方式的 ”自启动“。关于 SpringBoot 框架的这些机制的实现原理在后续章节详细分析,敬请期待。

tips:在 SpringBoot 框架的 Application 接口拓展实现类中,对于 Spring IOC 容器的 Java 对象的创建与依赖注入的流程还是与 Spring 框架保持一致的,SpringBoot 并没有对主流程进行修改,而是通过拓展实现来加入自身框架工作所需的相关功能,如以上所说的嵌入 Servlet 引擎组件等。

2 Spring IOC 容器的初始化流程

2.1 AbstractApplicationContext 的 refresh 方法:定义 IOC 容器的初始化流程

以上讲解了基于 Spring 框架的 Application 接口实现类 AnnotationConfigApplicationContext 来创建 Spring IOC 容器以及进行 Java 对象的管理。

由 Spring 框架的内部源码实现可知,Spring IOC 容器的创建和初始化是在抽象类 AbstractApplicationContext 的 refresh 方法中定义的,所以在以上例子中,在 BootApplication 的 main 方法中调用了 AnnotationConfigApplicationContext 的 refresh 方法。

Spring 框架在 AbstractApplicationContext 的 refresh 方法中定义了 Spring IOC 容器的初始化流程,refresh 方法的具体定义如下:

public void refresh() throws BeansException, IllegalStateException {
   // 加互斥锁,保证在任何时候,只有一个线程对一个 Application 实现类对象进行操作
   synchronized (this.startupShutdownMonitor) {
      // IOC容器相关周边属性的加载,如环境属性properties的加载等
      prepareRefresh();

      // 创建Bean对象工厂,加载Java对象的元数据并保存到对应的BeanDefinition对象
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 对Bean对象工厂进行预处理
      prepareBeanFactory(beanFactory);

      try {
         // Bean对象工厂的后置处理
         postProcessBeanFactory(beanFactory);
         invokeBeanFactoryPostProcessors(beanFactory);
        
         // 注册部分特殊的Bean对象后置处理器
         registerBeanPostProcessors(beanFactory);

         // i18相关国际化Message处理
         initMessageSource();

         // 初始化IOC容器事件广播器
         initApplicationEventMulticaster();

         // 提供给ApplicationContext接口的不同实现类来拓展实现,
         // 在该方法中自定义该实现类自身具有特殊含义 bean 对象的初始化方式
         onRefresh();

         // 调用IOC容器监听器
         registerListeners();

         // 实例化所有的单例对象,即此处完成单例对象的创建以及相关依赖注入
         finishBeanFactoryInitialization(beanFactory);

         // IOC容器初始化完成,分发相关的事件
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }
         // 删除已经创建的bean对象
         destroyBeans();
				 // 取消相关已经执行过的初始化流程
         cancelRefresh(ex);

         throw ex;
      }

      finally {
         // 重置相关内存缓存
         resetCommonCaches();
      }
   }
}

在 refesh 方法的内部实现当中,首先需要使用 synchronized 关键字来保证在任何时候,只能存在一个线程调用当前的 Application 接口实现类对象的 refresh 方法,即不能存在两个线程同时调用 refresh 方法来初始化同一个 IOC 容器。其次与 Java bean 对象的的创建相关的方法的调用依次如下:

  1. prepareRefresh 方法:完成外部属性的加载。如 properties 文件定义的属性值的加载,从而可以在后续对 Java 对象的属性进行赋值操作,如对 Java 对象中使用了 @Value 注解的属性进行赋值;
  2. obtainFreshBeanFactory 方法:完成 Java 对象对应的元数据的加载。对于每个 Java 对象都使用一个对应的 BeanDefinition 类的对象来维护,如从 XML 配置文件的 bean 标签解析,或者通过组件自动扫描的方式来加载,最终使用一个 BeanFactory 接口实现类对象,即 Bean 工厂来维护;
  3. postProcessBeanFactory 方法:对步骤 2 中创建的 Bean 工厂进行后置处理,如处理 @Configuration 注解的配置类,从该配置类中获取更多的 Java 对象的元数据,如在该配置类内部使用 @Bean 注解的方法会被加载和对应到一个 Java 对象;
  4. onRefresh 方法:该方法主要是提供了一种方式来给 Applicaiton 接口的不同实现类来拓展实现,自定义自身的具有特殊含义的 Java 对象的初始化过程,从而实现拓展性,这个也是 SpringBoot 框架自定义自身功能组件 对象的核心实现方法;
  5. finishBeanFactoryInitialization:以上过程只是完成了当前应用的相关 Java 对象的元数据的加载,但是并没有实际进行 Java 对象的创建,即没有调用如 new 关键字创建一个 Java 对象。所以这个方法是根据 Java 对象的元数据信息 BeanDefinition,完成单例 Java 对象的创建。

tips:由于 Spring 框架源码模块非常多,导致很多想学习 Spring 源码的同学不知道从哪个模块看起。根据我个人阅读 Spring 源码的经验,以上这个 refresh 方法是我们学习 Spring 框架源代码时需要重点关注的一个方法,也是 Spring 框架源码阅读的入口,即可以从这个方法展开对 Spring 框架各个模块的源代码的学习。

可以是说搞懂了以上介绍的 refresh 方法内部的各个调用的实现,那对 Spring 框架的学习也就差不多了_。

3 总结

在本小节我们分析了解 Spring IOC 容器的使用和设计对学习 SpringBoot 框架的重要性,即 SpringBoot 框架简化了 Spring 应用的开发难度。不过在 SpringBoot 的内部实现层面还是依赖 Spring 框架的 IOC 容器来进行 Java 对象的管理。所以为了更好的理解 SpringBoot 框架的整体工作原理,需要先了解 Spring 框架的 IOC 容器的使用和工作原理。

其次我们进一步分析了 Spring IOC 容器的创建和初始化流程。具体为对 Application 接口的抽象实现类 AbstractApplication 的 refresh 方法的分析。该方法定义了 IOC 容器的整体初始化流程。在该方法从上到下依次完成了环境属性的加载、Bean 对象工厂的创建和后置处理、Bean 对象元数据的加载、具有特殊含义 Bean 对象的处理,以及最终完成单例 bean 对象的创建与后置处理,如实现依赖注入。

代码仓库

1.2 节:https://github.com/yzxie/java-framework-demo/tree/master/spring-demo

点击这里复制本文地址 以上内容由nimo97整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

尼墨宝库 © All Rights Reserved.  蜀ICP备2024111239号-7