Spring的IoC容器启动过程之源码级分析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Spring的IoC容器启动过程之源码级分析

一、概述

Spring的IoC容器的启动过程,核心流程是将bean的配置项从不同渠道,包括XML、注解或者配置文件中读取和解析后,生成BeanDefinition的过程,在这过程中IoC容器会进行refresh操作,这个过程可以设置一些BeanPostProcesser的前置或后置操作,在执行完这些操作后,BeanDefinition就会被注册到BeanDefinitionRegistry容器中。

整体IoC容器的启动过程分为3个阶段:定位—>加载–>注册

  1. 定位: 通过ResourceLoader来完成资源的定位,后续的Bean配置项透视通过Resource资源文件来读取和解析的。
  2. 加载:ApplicationContext容器调用refresh()方法,解析Bean配置信息将其解析成BeanDefinition,然后调用一系列容器、Bean级别的前置后置处理器,包括调用BeanFactoryPostProcessor 的前置操作。
  3. 注册:BeanDefinition实例注册,将生成的BeanDefinition放到容器的缓存池BeanDefinitionRegistry中。

二、IoC容器介绍

首先必须要明确一点的就是BeanFactory作为所有IoC容器的顶级类,其包括哪些功能和层次结构,其中getBean(String name)是常见方法,通过该方法可以根据beanName获取Bean对象。

BeanFactory和其他IoC容器的层级关系如下。其中常见的是高级IoC容器ApplicationContext,该容器和BeanFactory相比,具有(1)能够响应时间机制;(2)具有国际化机制;

三、加载过程中常见类介绍

1.BeanDefinition

IoC容器管理的是Bean实例和其关系,在实现层面是通过BeanDefinition来进行描述的。

2.BeanDefinitionReader

BeanDefinitionReader是加载器和解析器,主要功能是从Resource资源类型中解析Bean实例出来,组装成BeanDefinition。

3.BeanDefinitionRegistry

BeanDefinition的注册表,实例化后的BeanDefinition会注册到这里。

四、源码层面分析IoC容器加载流程

IoC的的高级容器ApplicationContext有多种实现,现在以从XML中加载Bean的配置项的实例ClassPathXmlApplicationContext来具体说明IoC容器的加载流程。

1.定位阶段

通过XML配置文件启动一个ApplicationContext容器的方法。

public static void main(String[] args) {
    // 创建一个Spring容器
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    User user1 = (User) applicationContext.getBean("user1");
    user1.sayHello();
    }

2.加载阶段

通过ClassPathXmlApplicationContext类中的 refresh()进行刷新操作。

//ClassPathXmlApplicationContext.java
...
public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {
   super(parent);
   setConfigLocations(configLocations);
   if (refresh) {
      refresh();//my-刷新容器,生成BeanDefinition
   }
}
...

实现refresh()方法,其中核心是执行:obtainFreshBeanFactory() 方法,会创建一个BeanFactory的实例。同时在这个方法内也会进行一系列的初始化前或后的操作,比如执行方法:postProcessBeanFactory(beanFactory);

//AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
      // Prepare this context for refreshing.
      prepareRefresh();
      // Tell the subclass to refresh the internal bean factory.
      // 这里会判断能否刷新,并且返回一个BeanFactory, 刷新不代表完全情况,主要是先执行Bean的销毁,然后重新生成一个BeanFactory,再在接下来的步骤中重新去扫描等等
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // Prepare the bean factory for use in this context.
      // 准备BeanFactory
      // 1. 设置BeanFactory的类加载器、SpringEL表达式解析器、类型转化注册器
      // 2. 添加三个BeanPostProcessor,注意是具体的BeanPostProcessor实例对象
      // 3. 记录ignoreDependencyInterface
      // 4. 记录ResolvableDependency
      // 5. 添加三个单例Bean
      prepareBeanFactory(beanFactory);
      try {
         // Allows post-processing of the bean factory in context subclasses.
         // 子类来设置一下BeanFactory
         postProcessBeanFactory(beanFactory);
         StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
         // Invoke factory processors registered as beans in the context.
         // BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理
         // 默认情况下:
         // 此时beanFactory的beanDefinitionMap中有6个BeanDefinition,5个基础BeanDefinition+AppConfig的BeanDefinition
         // 而这6个中只有一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor
         // 这里会执行ConfigurationClassPostProcessor进行@Component的扫描,扫描得到BeanDefinition,并注册到beanFactory中
         // 注意:扫描的过程中可能又会扫描出其他的BeanFactoryPostProcessor,那么这些BeanFactoryPostProcessor也得在这一步执行
         invokeBeanFactoryPostProcessors(beanFactory);  // scanner.scan()
         // Register bean processors that intercept bean creation.
         // 将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去
         registerBeanPostProcessors(beanFactory);
         beanPostProcess.end();
         // Initialize message source for this context.
         // 设置ApplicationContext的MessageSource,要么是用户设置的,要么是DelegatingMessageSource
         initMessageSource();
         // Initialize event multicaster for this context.
         // 设置ApplicationContext的applicationEventMulticaster,要么是用户设置的,要么是SimpleApplicationEventMulticaster
         initApplicationEventMulticaster();
         // Initialize other special beans in specific context subclasses.
         // 给子类的模板方法
         onRefresh();
         // Check for listener beans and register them.
         // 把定义的ApplicationListener的Bean对象,设置到ApplicationContext中去,并执行在此之前所发布的事件
         registerListeners();
         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);
         // Last step: publish corresponding event.
         finishRefresh();
      }

具体执行加载BeanDefinition实例的方法。

//AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   refreshBeanFactory();
   return getBeanFactory();
}
...
//AbstractRefreshableApplicationContext.java
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
    }
    try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory);//my-加载BeanDefinition类
      this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
    }

需要从资源文件中读取和解析Bean的配置信息,主要通过XmlBeanDefinitionReader来实现。

//AbstractXmlApplicationContext.java
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
    initBeanDefinitionReader(beanDefinitionReader);//my-初始化XmlBeanDefinitionReader来
    loadBeanDefinitions(beanDefinitionReader);//my-通过XmlBeanDefinitionReader来读取BeanDefinition
}
...
//AbstractXmlApplicationContext.java
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

实际加载BeanDefinitions的方法。

//XmlBeanDefinitionReader.java
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isTraceEnabled()) {
      logger.trace("Loading XML bean definitions from " + encodedResource);
   }
   Set currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   //my-从当前目录中加载bean的配置项
   try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
      InputSource inputSource = new InputSource(inputStream);
      if (encodedResource.getEncoding() != null) {
         inputSource.setEncoding(encodedResource.getEncoding());
      }
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}
...
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isTraceEnabled()) {
      logger.trace("Loading XML bean definitions from " + encodedResource);
    }
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
          "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    //my-从当前目录中加载bean的配置项
    try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
      InputSource inputSource = new InputSource(inputStream);
      if (encodedResource.getEncoding() != null) {
        inputSource.setEncoding(encodedResource.getEncoding());
      }
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());//my-实际从XML中解析Bean
    }
    catch (IOException ex) {
      throw new BeanDefinitionStoreException(
          "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
        this.resourcesCurrentlyBeingLoaded.remove();
      }
    }
}

3.注册阶段

开始进行注册。

//XmlBeanDefinitionReader.java
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // spring.xml对应的Document对象
      Document doc = doLoadDocument(inputSource, resource);
      // 解析spring.xml
      int count = registerBeanDefinitions(doc, resource);
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + count + " bean definitions from " + resource);
      }
      return count;
   }

通过建立委托类BeanDefinitionParserDelegate来进行注册。

doRegisterBeanDefinitions(Element root) {
   // Any nested  elements will cause recursion in this method. In
   // order to propagate and preserve  default-* attributes correctly,
   // keep track of the current (parent) delegate, which may be null. Create
   // the new (child) delegate with a reference to the parent for fallback purposes,
   // then ultimately reset this.delegate back to its original (parent) reference.
   // this behavior emulates a stack of delegates without actually necessitating one.
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);
   if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         // We cannot use Profiles.of(...) since profile expressions are not supported
         // in XML config. See SPR-12458 for details.
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            if (logger.isDebugEnabled()) {
               logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }
   preProcessXml(root);
   parseBeanDefinitions(root, this.delegate);//my-进行注册的核心方法
   postProcessXml(root);

将BeanDefinition注册到IoC容器,其底层的数据结构还是HashMap。

//SimpleBeanDefinitionRegistry.java
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {
   Assert.hasText(beanName, "'beanName' must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");
   this.beanDefinitionMap.put(beanName, beanDefinition);
}

4.各阶段时序图

创建ClassPathXmlApplicationContext的IoC容器类的时序图。

Spring的IoC容器启动过程之源码级分析:https://www.processon.com/diagraming/645648cb40d4bf03caee856f

五、总结

最后总结一下,Spring IoC启动的过程就是将Bean组装成BeanDefinition保存在HashMap中的过程。

TODO

参考资料


目录
相关文章
|
11天前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
24 3
|
1月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
460 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
182 2
|
1月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
57 2
|
2月前
|
设计模式 Java Spring
spring源码设计模式分析(五)-策略模式
spring源码设计模式分析(五)-策略模式
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
113 5
|
2月前
|
消息中间件 设计模式 缓存
spring源码设计模式分析(四)-观察者模式
spring源码设计模式分析(四)-观察者模式
|
1月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
71 0
|
2月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
134 3
|
2月前
|
XML 存储 Java
Spring-源码深入分析(二)
Spring-源码深入分析(二)
下一篇
无影云桌面