详解Spring IOC 容器启动流程分析

使用 Spring 时,XML 和注解是使用得最多的两种配置方式,虽然是两种完全不同的配置方式,但对于 IOC 容器来说,两种方式的不同主要是在 BeanDefinition 的解析上。而对于核心的容器启动流程,仍然是一致的。

AbstractApplicationContext 的 refresh 方法实现了 IOC 容器启动的主要逻辑,启动流程中的关键步骤在源码中也可以对应到独立的方法。接下来以  AbstractApplicationContext 的实现类  ClassPathXmlApplicationContext 为主 ,并对比其另一个实现类 AnnotationConfigApplicationContext , 解读 IOC 容器的启动过程。

AbstractApplicationContext.refresh

@Override
 public void refresh() throws BeansException, IllegalStateException {
 synchronized (this.startupShutdownMonitor) {
  // Prepare this context for refreshing.
  prepareRefresh();

  // Tell the subclass to refresh the internal bean factory.
  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

  // Prepare the bean factory for use in this context.
  prepareBeanFactory(beanFactory);

  try {
  // Allows post-processing of the bean factory in context subclasses.
  postProcessBeanFactory(beanFactory);

  // Invoke factory processors registered as beans in the context.
  invokeBeanFactoryPostProcessors(beanFactory);

  // Register bean processors that intercept bean creation.
  registerBeanPostProcessors(beanFactory);

  // Initialize message source for this context.
  initMessageSource();

  // Initialize event multicaster for this context.
  initApplicationEventMulticaster();

  // Initialize other special beans in specific context subclasses.
  onRefresh();

  // Check for listener beans and register them.
  registerListeners();

  // Instantiate all remaining (non-lazy-init) singletons.
  finishBeanFactoryInitialization(beanFactory);

  // Last step: publish corresponding event.
  finishRefresh();
  }

  // ...
 }
 }

ApplicationContext 和 BeanFactory 的关系

ClassPathXmlApplicationContext 和  AnnotationConfigApplicationContext 的继承树如下所示。两者都继承自  AbstractApplicationContext 。

ApplicationContext 继承树( 高清大图 )

BeanFactory 继承树( 高清大图 )

ApplicationContext 是 IOC 容器的承载体,而  BeanFactory 是操作这个容器的工具,两者关系紧密,相互协作。 refresh 方法实现了 ApplicationContext 和 BeanFactory 相互协作的主要过程,不同之处主要在子类  AbstractRefreshableApplicationContext 和 GenericApplicationContext 中实现,两者使用的 BeanFactory 都为 DefaultListableBeanFactory , DefaultListableBeanFactory 的定义如下:

DefaultListableBeanFactory :

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
  implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable

可见 DefaultListableBeanFactory 实现了  ConfigurableListableBeanFactory ,意味着是可配置,可遍历的,至于为什么可以,让我们继续往下寻找找答案。

BeanDefinition 的获取

DefaultListableBeanFactory 中使用 Map 结构保存所有的 BeanDefinition 信息:

DefaultListableBeanFactory :

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

ClassPathXmlApplicationContext 中的解析
使用 BeanDefinitionDocumentReader (可参看 DefaultBeanDefinitionDocumentReader.processBeanDefinition 方法) 将 xml 中的 bean 解析为 BeanDefinition , 然后由 BeanDefinitionRegistry 注册到 BeanFactory 中。 入口: AbstractApplicationContext.refreshBeanFactory (在 refresh 中调用)

AnnotationConfigApplicationContext 中的解析
通过 BeanDefinitionScanner 扫描 Bean 声明,解析为 BeanDefinition 并由 BeanDefinitionRegistry 注册到 BeanFactory 中。 入口: AnnotationConfigApplicationContext 的构造函数。

为什么 ClassPathXmlApplicationContext 的入口是在 refreshBeanFactory 方法中 ?
AbstractApplicationContext.refreshBeanFactory 定义如下:

AbstractApplicationContext :

protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException

可见是一个抽象方法,具体实现在子类中。只有 "Refreshable" 的 BeanFactory 才会在该方法中实现具体操作,如  AbstractRefreshableApplicationContext :

AbstractRefreshableApplicationContext :

@Override
 protected final void refreshBeanFactory() throws BeansException {
 if (hasBeanFactory()) {
  destroyBeans();
  closeBeanFactory();
 }
 try {
  DefaultListableBeanFactory beanFactory = createBeanFactory();
  beanFactory.setSerializationId(getId());
  customizeBeanFactory(beanFactory);
  loadBeanDefinitions(beanFactory);
  synchronized (this.beanFactoryMonitor) {
  this.beanFactory = beanFactory;
  }
 }
 catch (IOException ex) {
  throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
 }
 }

可见 AbstractRefreshableApplicationContext.``refreshBeanFactory 方法会检查 BeanFactory 是否已经存在( hasBeanFactory ),已经存在就先销毁所有的 Bean( destoryBeans )并关闭( closeBeanFactory ) BeanFactory ,然后再创建( createBeanFactory )新的。 而  GenericApplicationContext.refreshBeanFactory 中会检查是否为第一次调用,不是就抛出异常,不执行其他逻辑,即  GenericApplicationContext 不是 "Refreshable"的。

主流程分析

refresh 方法在  AbstractApplicationContext 中定义,其中的  obtainFreshBeanFactory 方法调用了 getBeanFactory 方法,该方法用于获取  BeanFactory ,这里为  DefaultListableBeanFactory ,接下来无特别说明,大部分的方法和变量都将取自  AbstractApplicationContext 和   DefaultListableBeanFactory 。

高清大图

BeanPostProcessor

BeanPostProcessor 接口让开发者在 IOC 容器对 Bean 进行实例化时收到回调( postProcessAfterInitialization 和  postProcessBeforeInitialization 方法)。spring 框架内部的许多通知( Aware )就是通过这个接口实现,如  ApplicationContextAwareProcessor ,  ServletContextAwareProcessor ,他们的实现会在  postProcessBeforeInitialization 方法中进行检查,若实现了特定接口,就会调用 Aware 的回调方法,给予通知:

ServletContextAwareProcessor :

@Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
 if (getServletContext() != null && bean instanceof ServletContextAware) {
  ((ServletContextAware) bean).setServletContext(getServletContext());
 }
 if (getServletConfig() != null && bean instanceof ServletConfigAware) {
  ((ServletConfigAware) bean).setServletConfig(getServletConfig());
 }
 return bean;
 }

在 postProcessBeanFactory 方法中,子类可以通过 beanFactory.addBeanPostProcessor 方法添加自己的 BeanPostProcessor 到 beanFactory 中,最终将保存到 BeanFactory 的  beanPostProcessors (实为 CopyOnWriteArrayList ) 中。 prepareBeanFactory 和 registerBeanPostProcessors 方法是集中实例化并添加这些 Bean 的地方。

BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor
从"BeanDefinition 的获取"的介绍可以知道 BeanDefinitionRegistry 用于将  BeanDefinition 注册到 BeanFactory 中, GenericApplicationContext 和  DefaultListableBeanFactory 都实现了该接口, GenericApplicationContext 中的实现直接调用了 beanFactory 的实现。

BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 与  BeanPostProcessor 类似,但从他们的命名就可以看出,所针对的目标不同,分别是 BeanFactory 和  BeanDefinitionRegistry: 1 BeanFactoryPostProcessor 回调让开发者有机会在 BeanFactory 已经初始化好的情况下对 BeanFactory 的一些属性进行覆盖,或是对  beanDefinitionMap 中的  BeanDefinition 进行修改。 2 BeanDefinitionRegistryPostProcessor 则让开发者可以继续添加  BeanDefinition 到 BeanFactory 中。

具体逻辑在 invokeBeanFactoryPostProcessors 中实现,这里首先会将所有实现了 BeanFactoryPostProcessors 的 Bean 实例化,然后调用其回调方法( postProcessBeanDefinitionRegistry 或  postProcessBeanFactory 方法)。

对于这部分 Bean 的实例化和进行回调有一定的优先级规则。 PriorityOrdered 继承自 Ordered 接口,实现了  PriorityOrdered 的  BeanDefinitionRegistryPostProcessor 将最先被实例化并调用,然后同样的规则来回调实现了  BeanFactoryPostProcessor 的 Bean: PriorityOrdered > Ordered > 未实现 Ordered 的

在 registerBeanPostProcessors 方法中对 BeanPostProcessor 的实例化也有这样的优先级规则: PriorityOrdered > Ordered > 未实现 Ordered 的 > MergedBeanDefinitionPostProcessor

ApplicationEventMulticaster

在 initApplicationEventMulticaster 中会对  ApplicationEventMulticaster 进行初始化: 首先会检查是否已经有了 ApplicationEventMulticaster 的  BeanDefinition (在  beanDefinitionMap 中检查),有就让容器进行实例化,没有就使用框架默认的  ApplicationEventMulticaster (即 SimpleApplicationEventMulticaster ),先实例化,然后注册到容器中( MessageSource 在 initMessageSource 方法中也是同样的方式进行初始化)。

事件的起始发送处将事件包装为 ApplicationEvent ,并通过  ApplicationEventPublisher 提交给  ApplicationEventMulticaster , ApplicationEventMulticaster 会将事件广播给  ApplicationListener ,处理最终的分发。

AbstractApplicationEventMulticaster 中的 applicationListeners( 实为 LinkedHashSet<ApplicationListener>) 变量保存了所有的广播接收者, registerListeners 方法会将所有的  ApplicationListener 添加到该集合中。

finishRefresh 方法中有一个对  ContextRefreshedEvent 事件的广播可以作为参考,最终事件会由  multicastEvent 方法处理:

SimpleApplicationEventMulticaster.multicastEvent

@Override
 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 Executor executor = getTaskExecutor();
 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  if (executor != null) {
  executor.execute(() -> invokeListener(listener, event));
  }
  else {
  invokeListener(listener, event);
  }
 }
 }

那么在我们自己的 Bean 中如何得到这个 ApplicationEventPublisher 呢? ApplicationContext 的定义如下:

ApplicationContext :

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
 MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

可见 ApplicationContext 继承了  ApplicationEventPublisher ,这就说明  AbstractApplicationContext 也是一个  ApplicationEventPublisher 。在我们自己的 Bean 中通过实现  ApplicationEventPublisherAware ,我们就能通过  setApplicationEventPublisher 回调得到  ApplicationEventPublisher 。

上面我们提到 spring 的许多 Aware 是通过  BeanPostProcessor 实现的, ApplicationEventPublisherAware 也不例外:

ApplicationContextAwareProcessor :

@Override
 @Nullable
 public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
 // ...
    if (bean instanceof Aware) {
  if (bean instanceof EnvironmentAware) {
  ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
  }
  if (bean instanceof EmbeddedValueResolverAware) {
  ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
  }
  if (bean instanceof ResourceLoaderAware) {
  ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
  }
  if (bean instanceof ApplicationEventPublisherAware) {
  ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
  }
  if (bean instanceof MessageSourceAware) {
  ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
  }
  if (bean instanceof ApplicationContextAware) {
  ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
  }
 }
    // ...
 }

IOC 容器在实例化我们的 Bean 时会调用 ApplicationContextAwareProcessor . postProcessBeforeInitialization 方法,该方法会检查我们的 Bean,我们的 Bean 如果实现了  ApplicationEventPublisherAware ,那么就会回调  setApplicationEventPublisher 方法将  applicationContext (即 ApplicationEventPublisher ) 传给我们,我们就能够发布事件。

BeanFactory.getBean

BeanFactory 的几个重载了的 getBean 方法是 Bean 最终进行实例化的地方, registerBeanPostProcessors ,  invokeBeanFactoryPostProcessors 和  finishBeanFactoryInitialization 方法都调用了 getBean 方法对一些特定 Bean 进行了实例化。

finishBeanFactoryInitialization 中通过调用 BeanFactory 的  preInstantiateSingletons 对单例 Bean 进行实例化。 BeanFactory 和  BeanDefinition 都具有父子的概念,在子级找不到指定的 Bean 时将一直往上(父级)找,找到就进行实例化

总结

spring IOC 容器的启动步骤可总结如下: 1 初始化 ApplicationContext 环境属性的初始化和验证,启动时间记录和相关标记设置,应用事件和监听者的初始化。

2 准备好容器中的 BeanDefinition (eager-initializing beans) 对 BeanDefinition 的解析、扫描和注册, BeanDefinition 的扫描和注册大致可以分为 XML 和注解两种,两种方式各自使用的组件有所不同,该步骤的时间也可以在最前面。

3 初始化 BeanFactory 准备好 BeanFactory 以供 ApplicationContext 进行使用,对接下来将要使用到的 Bean 进行实例化,资源进行准备,属性进行设置。

4 注册 BeanPostProcessors BeanPostProcessors 是进行扩展的关键组件,需要在该步骤中进行注册,可分为两种类型: 一种是框架使用者提供的,用于特定业务功能的,另一种是框架开发者提供的,用于扩展框架功能。

5 调用 BeanDefinitionRegistryPostProcessor BeanDefinitionRegistryPostProcessor 是一种功能增强,可以在这个步骤添加新的  BeanDefinition 到 BeanFactory 中。

6 调用 BeanFactoryPostProcessor BeanFactoryPostProcessor 是一种功能增强,可以在这个步骤对已经完成初始化的 BeanFactory 进行属性覆盖,或是修改已经注册到 BeanFactory 的  BeanDefinition 。

7 初始化 MessageSource 和  ApplicationEventMulticaster MessageSource 用于处理国际化资源, ApplicationEventMulticaster 是应用事件广播器,用于分发应用事件给监听者。

8 初始化其他 Bean 和进行其他的的上下文初始化 主要用于扩展

9 注册 ApplicationListene 将  ApplicationListene 注册到 BeanFactory 中,以便后续的事件分发

10 实例化剩余的 Bean 单例 步骤 4 到 9 都对一些特殊的 Bean 进行了实例化,这里需要对所有剩余的单例 Bean 进行实例化

11 启动完成 资源回收,分发"刷新完成"事件。

总结

以上所述是小编给大家介绍的Spring IOC 容器启动流程分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • 浅析Java的Spring框架中IOC容器容器的应用

    Spring容器是Spring框架的核心.容器将创建对象,它们连接在一起,配置它们,并从创建到销毁管理他们的整个生命周期.在Spring容器使用依赖注入(DI)来管理组成应用程序的组件.这些对象被称为Spring Beans. 容器获得其上的哪些对象进行实例化,配置和组装通过阅读提供的配置元数据的说明.配置元数据可以通过XML,Java注释或Java代码来表示.下面的图是Spring如何工作的高层次图. Spring IoC容器是利用Java的POJO类和配置元数据的产生完全配置和可执行的系统或

  • 深入理解Java的Spring框架中的IOC容器

    Spring IOC的原型 spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功完成了依赖的反转:从主类的对依赖的主动管理反转为了spring容器对依赖的全局控制. 这样做的好处是什么呢? 当然就是所谓的"解耦"了,可以使得程序的各模块之间的关系更为独立,只需要spring控制这些模块之间的依赖关系并在容器启动和初始化的过程中将依据这些依赖关系创建.管理和维护这些模块就好,如果需要改变模块间的依赖关系的话,甚至都不需要改变程序代码,只需要将

  • 用java的spring实现一个简单的IOC容器示例代码

    要想深入的理解IOC的技术原理,没有什么能比的上我们自己实现它.这次我们一起实现一个简单IOC容器.让大家更容易理解Spring IOC的基本原理. 这里会涉及到一些java反射的知识,如果有不了解的,可以自己去找些资料看看. 注意 在上一篇文章,我说,启动IOC容器时,Spring会将xml文件里面配置的bean扫描并实例化,其实这种说法不太准确,所以我在这里更正一下,xml文件里面配置的非单利模式的bean,会在第一次调用的时候被初始化,而不是启动容器的时候初始化.但是我们这次要做的例子是容

  • 关于SpringBoot获取IOC容器中注入的Bean(推荐)

    一: 注入一个TestUtils类 package com.shop.sell.Utils; import com.shop.sell.dto.CartDTO; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TestUtils { @Bean(name="test

  • 浅谈Spring IoC容器的依赖注入原理

    本文介绍了浅谈Spring IoC容器的依赖注入原理,分享给大家,具体如下: IoC容器初始化的过程,主要完成的工作是在IoC容器中建立 BeanDefinition 数据映射,并没有看到IoC容器对Bean依赖关系进行注入, 假设当前IoC容器已经载入用户定义的Bean信息,依赖注入主要发生在两个阶段 正常情况下,由用户第一次向IoC容器索要Bean时触发 但我们可以在 BeanDefinition 信息中通过控制 lazy-init 属性来让容器完成对Bean的预实例化,即在初始化的过程中就

  • 详解Spring IOC 容器启动流程分析

    使用 Spring 时,XML 和注解是使用得最多的两种配置方式,虽然是两种完全不同的配置方式,但对于 IOC 容器来说,两种方式的不同主要是在 BeanDefinition 的解析上.而对于核心的容器启动流程,仍然是一致的. AbstractApplicationContext 的 refresh 方法实现了 IOC 容器启动的主要逻辑,启动流程中的关键步骤在源码中也可以对应到独立的方法.接下来以  AbstractApplicationContext 的实现类  ClassPathXmlAp

  • 详解spring boot应用启动原理分析

    前言 本文分析的是spring boot 1.3. 的工作原理.spring boot 1.4. 之后打包结构发现了变化,增加了BOOT-INF目录,但是基本原理还是不变的. 关于spring boot 1.4.* 里ClassLoader的变化,可以参考://www.jb51.net/article/141479.htm spring boot quick start 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动

  • spring boot容器启动流程

    一.前言 spring cloud大行其道的当下,如果不了解基本原理那么是很纠结的(看见的都是 约定大于配置 ,但是原理呢?为什么要这么做?).spring cloud是基于spring boot快速搭建的,今天咱们就看看spring boot容器启动流程.(本文不讲解如何快速启动spring boot,那些直接官方看即可, 官网文档飞机票 ) 二.容器启动 spring boot一般是 指定容器启动main方法,然后以命令行方式启动Jar包 ,如下图: @SpringBootApplicati

  • 详解Android Activity的启动流程

    前言 activity启动的流程分为两部分:一是在activity中通过startActivity(Intent intent)方法启动一个Activity:二是我们在桌面通过点击应用图标启动一个App然后显示Activity:第二种方式相较于第一种方式更加全面,所以本文会以第二种流程来分析. 简要 我们手机的桌面是一个叫做Launcher的Activity,它罗列了手机中的应用图标,图标中包含安装apk时解析的应用默认启动页等信息.在点击应用图标时,即将要启动的App和Launcher.AMS

  • 详解docker nginx 容器启动挂载到本地

    首先nginx容器内部的结构: 进入容器: docker exec -it b511b6049f57 bash 查看容器的结构目录:其实每一个容器就相当于一个独立的系统. root@b511b6049f57:/# ls bin dev home lib64 mnt proc run srv tmp var boot etc lib media opt root sbin sys usr nginx的结构目录在容器中: 日志位置:/var/log/nginx/ 配置文件位置:/etc/nginx/

  • 详解spring boot容器加载完后执行特定操作

    有时候我们需要在spring boot容器启动并加载完后,开一些线程或者一些程序来干某些事情.这时候我们需要配置ContextRefreshedEvent事件来实现我们要做的事情 1.ApplicationStartup类 public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent>{ public void onApplicationEvent(ContextRefreshedEve

  • 详解Spring简单容器中的Bean基本加载过程

    本篇将对定义在 XMl 文件中的 bean,从静态的的定义到变成可以使用的对象的过程,即 bean 的加载和获取的过程进行一个整体的了解,不去深究,点到为止,只求对 Spring IOC 的实现过程有一个整体的感知,具体实现细节留到后面用针对性的篇章进行讲解. 首先我们来引入一个 Spring 入门使用示例,假设我们现在定义了一个类 org.zhenchao.framework.MyBean ,我们希望利用 Spring 来管理类对象,这里我们利用 Spring 经典的 XMl 配置文件形式进行

  • 详解spring注解配置启动过程

    最近看起spring源码,突然想知道没有web.xml的配置,spring是怎么通过一个继承于AbstractAnnotationConfigDispatcherServletInitializer的类来启动自己的.鉴于能力有限以及第一次看源码和发博客,不到之处请望谅~ 我用的IDE是IntelliJ IDEA,这个比myEclipse看源码方便一点,而且黑色背景挺喜欢.然后项目是在maven下的tomcat7插件运行.spring版本是4.3.2.RELEASE. 如果写过纯注解配置的spri

  • 详解Spring Boot 项目启动时执行特定方法

    Springboot给我们提供了两种"开机启动"某些方法的方式:ApplicationRunner和CommandLineRunner. 这两种方法提供的目的是为了满足,在项目启动的时候立刻执行某些方法.我们可以通过实现ApplicationRunner和CommandLineRunner,来实现,他们都是在SpringApplication 执行之后开始执行的. CommandLineRunner接口可以用来接收字符串数组的命令行参数,ApplicationRunner 是使用App

  • 详解Spring注解 @Configuration

    目录 @Configuration 注解的概述 底层原理 与 Spring IoC 容器的集成 Bean 的定义和装配的实现 条件化配置的实现 配置类的加载和实例化过程 总结 Spring 提供了丰富的特性和功能,包括依赖注入.面向切面编程.事务管理.数据访问.Web应用程序开发等.其中,@Configuration 是 Spring 中的一个注解,它用于标记一个类为配置类,通过配置类可以定义和组装 Spring Bean,并且支持高度灵活的配置方式.在本文中,我们将深入探讨 @Configur

随机推荐