SpringBoot版本:2.1.1      ==》启动流程分析汇总

接上篇博客Spring Boot 2.1.1(十一)启动流程分析之设置系统属性spring.beaninfo.ignore、自定义banner图

目录

流程分析

1、AbstractApplicationContext

2、GenericApplicationContext

2.1、DefaultListableBeanFactory

2.2、bean实例化策略

3、AnnotationConfigServletWebServerApplicationContext

3.1、实例化AnnotatedBeanDefinitionReader

3.2、实例化ClassPathBeanDefinitionScanner

总结


public ConfigurableApplicationContext run(String... args) {.... try {//本篇内容从本行开始记录context = createApplicationContext();//本篇内容记录到这,后续更新....}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}
}

流程分析

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;//判断当前applicationContextClass是否为空,为空则根据web应用类型创建上下文if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}//最终转换成是ConfigurableApplicationContextreturn (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

根据应用类型创建对应的ApplicationContext对象,这里最终创建的是AnnotationConfigServletWebServerApplicationContext。

要了解AnnotationConfigServletWebServerApplicationContext的实例化过程中都做了什么,先要了解其继承结构。下面是其类图。

会先从父类开始加载,追寻构造方法一直到AbstractApplicationContext。只在AbstractApplicationContextGenericApplicationContext、以及AnnotationConfigServletWebServerApplicationContext中构造方法执行了操作。下面一个一个记录执行的操作。

1、AbstractApplicationContext

AbstractApplicationContext中会做一些初始化,包括:

  1. 会得到Log对象
  2. 为上下文设置唯一id和显示名称
  3. 初始化BeanFactoryPostProcessors 的List集合
  4. 是否活跃标识active,1为true,0为false
  5. 是否已关闭标识closed,1为true,0为false
  6. 刷新和销毁的同步对象startupShutdownMonitor
  7. 初始化ApplicationListener<?>的Set集合
  8. 在构造方法中实例化一个ServletContextResourcePatternResolver,参数就是AnnotationConfigServletWebServerApplicationContext

在构造方法中调用的方法是子类GenericWebApplicationContext重写后的方法。 将AnnotationConfigServletWebServerApplicationContext作为一个resourceLoader传递到父类。

父类PathMatchingResourcePatternResolver是一个Ant模式通配符的Resource查找器,可以用来查找类路径下或者文件系统中的资源。在其中会实例化一个AntPathMatcher,该类实现Ant风格的路径模式。

解释一下BeanFactoryPostProcessors接口:

BeanFactoryPostProcessors:通过实现该接口,重写postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法,可以在bean未实例化之前通过beanFactory得到bean定义,并可以修改属性值。

2、GenericApplicationContext

然后在GenericApplicationContext中同样会做一些初始化,包括:

  1. 是否刷新标识refreshed,1为true,0为false
  2. customClassLoader = false
  3. 在构造方法中会实例化一个DefaultListableBeanFactory

官方注释里说了,GenericApplicationContext与为每次刷新创建新的内部beanfactory实例的其他applicationContext实现不同,此上下文的内部beanfactory从一开始就可用,以便能够在其上注册bean定义。只能调用一次refresh()。

在后面刷新上下文的时候,相对于其他上下文在每次刷新的时候都重新创建一个BeanFactory,GenericApplicationContext则不用,在其刷新BeanFactory的方法中,方法上的注释说了Do nothing,什么都不做。在后面的上下文刷新中会详细记录。

2.1、DefaultListableBeanFactory

DefaultListableBeanFactory的类图如下(跟上面的类图本想画在一起的,奈何wps要会员,就只能画在另一个里面了):

同样会先从父类SimpleAliasRegistry开始加载。其中会初始化各种集合,包括存放单例bean对象,单例工厂bean,已注册的单例bean,已创建过的bean名称,bean定义等等以及接口集合,如BeanPostProcessor。具体的话看类图吧。

再看下BeanFactory接口的官方注释:

大概意思是:用于访问SpringBean容器的根接口。这是bean容器的基本客户机视图;其他接口(如listablebeanfactory和configurablebeanfactory)可用于特定目的。这个接口是由持有许多bean定义的对象实现的,每个定义都由一个字符串名称唯一标识。根据bean定义,工厂将返回包含对象的独立实例(原型模式)或单个共享实例(与工厂范围内的单个实例为单例的单例模式相比,这是一种更好的选择)。返回哪种类型的实例取决于bean工厂配置:API是相同的。自Spring2.0以来,根据具体的应用程序上下文(例如Web环境中的“请求”和“会话”范围),可以使用更多的范围。

大概意思是:

bean工厂实现应该尽可能支持标准bean生命周期接口。整套初始化方法及其标准顺序为:

  1. BeanNameAware's {@code setBeanName}
  2. BeanClassLoaderAware's {@code setBeanClassLoader}
  3. BeanFactoryAware's {@code setBeanFactory}
  4. EnvironmentAware's {@code setEnvironment}
  5. EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
  6. ResourceLoaderAware's {@code setResourceLoader}(only applicable when running in an application context)
  7. ApplicationEventPublisherAware's {@code setApplicationEventPublisher}(only applicable when running in an application context)
  8. MessageSourceAware's {@code setMessageSource}(only applicable when running in an application context)
  9. ApplicationContextAware's {@code setApplicationContext}(only applicable when running in an application context)
  10. ServletContextAware's {@code setServletContext}(only applicable when running in a web application context)
  11. {@code postProcessBeforeInitialization} methods of BeanPostProcessors
  12. InitializingBean's {@code afterPropertiesSet}
  13. a custom init-method definition
  14. {@code postProcessAfterInitialization} methods of BeanPostProcessors

在bean工厂关闭时,应用以下生命周期方法:

  1. {@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
  2. DisposableBean's {@code destroy}
  3. a custom destroy-method definition

BeanDefinitionRegistry:包含bean定义的注册表接口,例如RootBeanDefinition和ChildBeanDefinition实例。通常由Bean工厂实现,Bean工厂内部使用AbstractBeanDefinition层次结构。这是Spring的bean工厂包中唯一封装bean定义注册的接口 。标准BeanFactory接口仅涵盖对完全配置的工厂实例的访问。Spring的bean definition readers希望能够在这个接口的实现上工作。Spring中已知的核心实现是DefaultListableBeanFactory和GenericApplicationContext。

2.2、bean实例化策略

AbstractAutowireCapableBeanFactory中实例化了一个InstantiationStrategy接口的实现类对象,bean实例化策略,

根接口InstantiationStrategy只有三个重载方法。

继承结构如下:

可以看到重写了上面的三个方法。

挑一个短一点的方法看一下,如果没有方法重写,不需要生成cglib动态生成子类。否则就是调用的下面的方法,下面的instantiateWithMethodInjection方法是一个空方法。方法上的注释意思是:如果子类可以用给定rootbeanDefinition中指定的方法注入来实例化对象,则子类可以重写此方法,该方法实现为引发UnsupportedOperationException。实例化应该使用给定的构造函数和参数。

在看下子类重写的instantiateWithMethodInjection方法。只是稍微看下里面的内容。具体看调用的地方。

调用instantiate方法,然后再该方法内判断使用哪种实例化策略。可能表达的意思不是很好,主要想说就是这里是一种设计模式,策略模式

3、AnnotationConfigServletWebServerApplicationContext

在构造方法中会实例化两个类,分别是AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner。

3.1、实例化AnnotatedBeanDefinitionReader

看一下官方注释:

大概意思:方便的适配器,用于编程注册带注释的bean类。这是@link classpathbeanddefinitionscanner的替代方案,应用相同的注释解析,但仅限于显式注册的类。

在这个类中会实例化一个AnnotationBeanNameGenerator和一个AnnotationScopeMetadataResolver。

AnnotationBeanNameGenerator见名知意,生成beanName。如果属于注解bean定义,即获取注解定义的beanName,不是就获取默认的beanName,即类名首字母小写。isStereotypeWithNameValue()方法会判断注解类型是否是Component或者元注解中是否有Component或者注解类型是否是ManagedBean和Named,且attributes不为空,attributes存在value值。

AnnotationScopeMetadataResolver即Scope注解元数据解析。扫描Scope注解并设置值。

是否属于注解定义,是就转为注解定义并得到attributes对象,该对象包含了注解信息,如果attributes不为空,得到value值即Scope的取值,并设置到ScopeName,再设置代理模式,最后返回Scopemetadata对象。

构造方法

上面看官方注释的时候说了方便的适配器,在构造方法中的参数类型是BeanDefinitionRegistry,实际传进来的是AnnotationConfigServletWebServerApplicationContext,这就是对象的适配。通过看类图可以知道AnnotationConfigServletWebServerApplicationContext的父类GenericApplicationContext实现了BeanDefinitionRegistry接口。所以这里AnnotationConfigServletWebServerApplicationContext可以被当做一个registry。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");Assert.notNull(environment, "Environment must not be null");//用于bean定义注册this.registry = registry;//用于判断是否跳过bean注册,用于判断Condition注解this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);//在这一步会注册几个bean定义AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

在registerAnnotationConfigProcessors()方法中,设置了DependencyComparator(依赖比较器),AutowireCandidateResolver(Autowire注解候选解析,即解析@Qualifier注解),以及注册了几个bean定义。AnnotationConfigServletWebServerApplicationContext被当做registry用来注册bean定义,实际调用的是DefaultListableBeanFactory的registerBeanDefinition方法。

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {registerAnnotationConfigProcessors(registry, null);
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);if (beanFactory != null) {if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));}// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition();try {def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,AnnotationConfigUtils.class.getClassLoader()));}catch (ClassNotFoundException ex) {throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);}def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));}if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));}return beanDefs;
}

注册的bean如下 :

  1. ConfigurationClassPostProcessor
  2. AutowiredAnnotationBeanPostProcessor
  3. CommonAnnotationBeanPostProcessor
  4. EventListenerMethodProcessor
  5. DefaultEventListenerFactory

这些注册的bean会在后面的流程中被调用。

3.2、实例化ClassPathBeanDefinitionScanner

同样先看下官方注释

大概意思:

一个bean定义扫描器,用于检测类路径上的bean候选项,并在给定的注册表(@code beanfactory或@code applicationContext)中注册相应的bean定义。

通过可配置的类型过滤器检测候选类。默认过滤器包括用Spring的@Component、@Repository、@Service、@Controller注释的类。

同样支持JavaEE 6的ManagedBean和JSR-330的Named注解,如果有的话。

该类中同样有BeanDefinitionRegistry,AnnotationBeanNameGenerator,AnnotationScopeMetadataResolver对象。

构造方法

在父类ClassPathScanningCandidateComponentProvider中初始化了两个final修饰的list,分别是List<TypeFilter> includeFilters和List<TypeFilter> excludeFilters。这两个List对象在isCandidateComponent()方法中充当过滤规则。

在registerDefaultFilters()方法中就往includeFilters中注册Filter,注册的是AnnotationTypeFilter,过滤的注解就是前面注释里讲的@Component、@Repository、@Service、@Controller和JavaEE 6的ManagedBean和JSR-330的Named注解。

然后就是设置环境,设置ResourceLoader。

到这AnnotationConfigServletWebServerApplicationContext创建过程就走完了。在创建上下文中注册的bean只有这五个,即在实例化AnnotatedBeanDefinitionReader的时候注册的。

总结

回顾一下流程:

AbstractApplicationContext中会做一些初始化,包括:

  1. 会得到Log对象
  2. 为上下文设置唯一id和显示名称
  3. 初始化BeanFactoryPostProcessors 的List集合
  4. 是否活跃标识active,1为true,0为false
  5. 是否已关闭标识closed,1为true,0为false
  6. 刷新和销毁的同步对象startupShutdownMonitor
  7. 初始化ApplicationListener<?>的Set集合
  8. 在构造方法中实例化一个ServletContextResourcePatternResolver,参数就是AnnotationConfigServletWebServerApplicationContext

GenericApplicationContext中同样会做一些初始化,包括:

  1. 是否刷新标识refreshed,1为true,0为false
  2. customClassLoader = false
  3. 在构造方法中会实例化一个DefaultListableBeanFactory

在AnnotationConfigServletWebServerApplicationContext中:

  1. 实例化AnnotatedBeanDefinitionReader
  2. 实例化ClassPathBeanDefinitionScanner

SpringBoot(十二)启动流程分析之创建应用上下文AnnotationConfigServletWebServerApplicationContext相关推荐

  1. Alian解读SpringBoot 2.6.0 源码(六):启动流程分析之创建应用上下文

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.创建应用上下文 2.1.初始化入口 2.2.初始化AbstractApplicationContext 2.3.初始化Generi ...

  2. Alian解读SpringBoot 2.6.0 源码(七):启动流程分析之准备应用上下文

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.准备应用上下文 2.1.整体流程 2.2.设置环境 2.3.应用上下文进行后置处理 2.4.应用所有初始化器 2.5.发布应用上下 ...

  3. Alian解读SpringBoot 2.6.0 源码(八):启动流程分析之刷新应用上下文(上)

    目录 一.背景 1.1.run方法整体流程 1.2.刷新的整体调用流程 1.3.本文解读范围 二.准备刷新 2.1.准备刷新的流程 2.2.初始化上下文环境中servlet相关属性源 2.3.校验re ...

  4. Alian解读SpringBoot 2.6.0 源码(八):启动流程分析之刷新应用上下文(下)

    目录 一.背景 1.1.刷新的整体调用流程 1.2.本文解读范围 二.初始化特定上下文子类中的其他特殊bean 2.1.初始化主体资源 2.2.创建web服务 三.检查监听器bean并注册它们 四.实 ...

  5. Alian解读SpringBoot 2.6.0 源码(八):启动流程分析之刷新应用上下文(中)

    目录 一.背景 1.1.刷新的整体调用流程 1.2.本文解读范围 二.调用后处理器 2.1.调用在上下文中注册为beanFactory的后置处理器 2.2.invokeBeanFactoryPostP ...

  6. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  7. Springboot启动流程分析(四):完成启动流程

    目录 一 添加BeanPostProcessors到IOC容器 二 国际化支持 三 初始化监听器的多路播放器 四 刷新容器 五 注册监听器到IOC容器的多播器 六 完成bean的大规模实例化 6.1 ...

  8. Alian解读SpringBoot 2.6.0 源码(九):启动流程分析之应用上下文刷新后处理(启动完成事件,Runner运行器,就绪事件)

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.应用上下文刷新后置处理 三.时间信息.输出日志记录执行主类名 四.发布应用上下文启动完成事件 4.1.ApplicationSta ...

  9. Alian解读SpringBoot 2.6.0 源码(四):启动流程分析之应用环境准备

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.应用环境准备 2.1.准备环境的整体流程 2.2.创建环境 2.3.配置环境 2.3.1.注册默认的转换器.格式化组件 2.3.1 ...

最新文章

  1. 算法---------x 的平方根
  2. ECS控制台使用小贴士
  3. 05用线程类Thread开启线程
  4. 程序员的时间管理哲学 —— 如何更好的利用我们的时间
  5. 【方法杂谈】你真的了解CVPR吗?
  6. VS Code 1.40 发布!可自行搭建 Web 版 VS Code!
  7. 【转】1:C#的三种异步的详细介绍及实现
  8. Robocode教程3——Robo机器剖析
  9. 微信小程序毕业设计 就餐预约点餐小程序毕业设计
  10. mysql+orm+odb_C++ ORM ODB 入门介绍(一)
  11. html表格冻结原理,html表格table冻结行和列
  12. php的vget方法,ext_lanzou.php
  13. 移动二班 21号 pycharm 04.09
  14. Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)
  15. 网站推广之搜索引擎篇(转)
  16. 整合开源治理经验,共谋开源社区发展|2023 开放原子全球开源峰会开源社区治理与运营分论坛即将启幕
  17. 国内外计算机视觉的差距在哪里?
  18. 微信支付 postman_微信开放支付分查询功能,凭分值可享受超1000种信用服务!
  19. 关于java用Quertz定时进行数据库同步的简单操作
  20. Vue常见问题汇总及解决方案

热门文章

  1. 解析 Java 类和对象的初始化过程(zhuang张 国建 (guojian.zhang@gmail
  2. 网站抢单搭建什么服务器,12306抢票、嘀嘀抢单服务器如何搭建?
  3. mybatis 实体嵌套查询
  4. 关于简书签约作者饱醉豚违反简书社区原则的公示说明
  5. Chromium-Dev中一些英文缩写
  6. 2年java,华为面试,一面 挂
  7. 计算机专业硕士英文,硕士研究生专业(领域)中英文对照(2015版).doc
  8. 全面预算管控 提升企业运营管理竞争力
  9. linux内核栈与用户栈及调用栈观察方法
  10. JAVA基础 练习--运动员和教练