1. The Bootstrap Application Context概念

这里是引用
A Spring Cloud application operates by creating a “bootstrap” context, which is a parent context for the main application. It is responsible for loading configuration properties from the external sources and for decrypting properties in the local external configuration files. The two contexts share an Environment, which is the source of external properties for any Spring application. By default, bootstrap properties (not bootstrap.properties but properties that are loaded during the bootstrap phase) are added with high precedence, so they cannot be overridden by local configuration.

翻译下意思:
Spring云应用程序bootstrap context进行操作(bootstrap context是主应用程序的父上下文)。bootstrap context负责从外部源加载配置属性,并解密本地外部配置文件中的属性。这两个上下文共享一个环境,该环境是任何Spring应用程序外部属性的来源。默认情况下,bootstrap属性(不是bootstrap.properties而是引导阶段加载的属性)具有较高的优先级,因此不能被本地配置覆盖。

截个图展示一下主上下文和bootstrap上下文

有了上图的直接认识后,我们来一步一步梳理下是怎么生成bootstrap context的
2. 入口代码

@EnableAutoConfiguration
public class SpringBootApplicationBootstrap {public static void main(String[] args) {new SpringApplicationBuilder(SpringBootApplicationBootstrap.class).run(args);}
}

SpringApplicationBuilder run方法org.springframework.boot.builder.SpringApplicationBuilder#run 进去

public ConfigurableApplicationContext run(String... args) {if (this.running.get()) {// If already created we just return the existing contextreturn this.context;}configureAsChildIfNecessary(args);if (this.running.compareAndSet(false, true)) {synchronized (this.running) {// If not already running copy the sources over and then run.// 这里进去就是SpringApplication 的run方法this.context = build().run(args);}}return this.context;}

org.springframework.boot.SpringApplication#run(java.lang.String…) 方法进去, 这个就springboot启动的主方法,会将几个关键路径梳理

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//1. 准备环境, 同时会进行广播事件,  广播到org.springframework.cloud.bootstrap.BootstrapApplicationListener  监听,// BootstrapApplicationListener监听器很重要, 监听器中会启动bootstrapcontext      ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);// 2. 准备上下文时会,会设置主上下的parent为bootstrap 上下文       prepareContext(context, environment, listeners, applicationArguments,printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}

3.org.springframework.boot.SpringApplication#prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());// 1. 广播事件,最终会调到org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)listeners.environmentPrepared(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;}

如下图,广播事件给监听器, 其中标出的就是org.springframework.cloud.bootstrap.BootstrapApplicationListener

org.springframework.cloud.bootstrap.BootstrapApplicationListener 是在spring.factories里配置的


在构造SpringApplication的时候通过spi的方式加载的

4. org.springframework.cloud.bootstrap.BootstrapApplicationListener 的监听方法

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {ConfigurableEnvironment environment = event.getEnvironment();if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,true)) {return;}// don't listen to events in a bootstrap contextif (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {return;}ConfigurableApplicationContext context = null;String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) {if (initializer instanceof ParentContextApplicationContextInitializer) {context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer,configName);}}if (context == null) {// 启动bootstrap上下文,传参event.getSpringApplication()就是主程序上下文context = bootstrapServiceContext(environment, event.getSpringApplication(),configName);}apply(context, event.getSpringApplication(), environment);}

org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext 启动bootstrap上下文

private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment, final SpringApplication application,String configName) {StandardEnvironment bootstrapEnvironment = new StandardEnvironment();MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();for (PropertySource<?> source : bootstrapProperties) {bootstrapProperties.remove(source.getName());}String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");Map<String, Object> bootstrapMap = new HashMap<>();bootstrapMap.put("spring.config.name", configName);// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail// force the environment to use none, because if though it is set below in the builder// the environment overrides itbootstrapMap.put("spring.main.web-application-type", "none");if (StringUtils.hasText(configLocation)) {bootstrapMap.put("spring.config.location", configLocation);}bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));for (PropertySource<?> source : environment.getPropertySources()) {if (source instanceof StubPropertySource) {continue;}bootstrapProperties.addLast(source);}ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicatesList<String> names = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(BootstrapConfiguration.class, classLoader));for (String name : StringUtils.commaDelimitedListToStringArray(environment.getProperty("spring.cloud.bootstrap.sources", ""))) {names.add(name);}// TODO: is it possible or sensible to share a ResourceLoader?// 创建 SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF).environment(bootstrapEnvironment)// Don't use the default properties in this builder.registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);final SpringApplication builderApplication = builder.application();if(builderApplication.getMainApplicationClass() == null){// gh_425:// SpringApplication cannot deduce the MainApplicationClass here// if it is booted from SpringBootServletInitializer due to the// absense of the "main" method in stackTraces.// But luckily this method's second parameter "application" here// carries the real MainApplicationClass which has been explicitly// set by SpringBootServletInitializer itself already.builder.main(application.getMainApplicationClass());}if (environment.getPropertySources().contains("refreshArgs")) {// If we are doing a context refresh, really we only want to refresh the// Environment, and there are some toxic listeners (like the// LoggingApplicationListener) that affect global static state, so we need a// way to switch those off.builderApplication.setListeners(filterListeners(builderApplication.getListeners()));}List<Class<?>> sources = new ArrayList<>();for (String name : names) {Class<?> cls = ClassUtils.resolveClassName(name, null);try {cls.getDeclaredAnnotations();}catch (Exception e) {continue;}sources.add(cls);}AnnotationAwareOrderComparator.sort(sources);builder.sources(sources.toArray(new Class[sources.size()]));// bootstrap上下文SpringApplicationBuilder 的run方法final ConfigurableApplicationContext context = builder.run();// gh-214 using spring.application.name=bootstrap to set the context id via// `ContextIdApplicationContextInitializer` prevents apps from getting the actual// spring.application.name// during the bootstrap phase.context.setId("bootstrap");// Make the bootstrap context a parent of the app context//  创建主程序application的初始化器,如下图; 会将bootstrap context放到初始化器的parent中,初始化器再将parent传给主程序上下文addAncestorInitializer(application, context);// It only has properties in it now that we don't want in the parent so remove// it (and it will be added back later)bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);return context;}


主application新增的初始化器

  1. 初始化器再将parent传给主程序上下文,org.springframework.boot.SpringApplication#prepareContext方法内调用初始化器
private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);postProcessApplicationContext(context);// 应用初始化器applyInitializers(context);listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);}

org.springframework.cloud.bootstrap.BootstrapApplicationListener.AncestorInitializer#initialize 调用BootstrapApplicationListener.AncestorInitializer的initialize方法进行初始化

public void initialize(ConfigurableApplicationContext context) {while (context.getParent() != null && context.getParent() != context) {context = (ConfigurableApplicationContext) context.getParent();}reorderSources(context.getEnvironment());// 实际是调用的ParentContextApplicationContextInitializer类的初始化方法, 这里parent就是bootstrap, context就是主程序应用上下文new ParentContextApplicationContextInitializer(this.parent).initialize(context);}

org.springframework.boot.builder.ParentContextApplicationContextInitializer的初始化方法

@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {if (applicationContext != this.parent) {// 设置parentapplicationContext.setParent(this.parent);applicationContext.addApplicationListener(EventPublisher.INSTANCE);}}

总结:

  1. 构建SpringApplication时会将BootstrapApplicationListener监听器通过spi方式加载并注册
  2. 主SpringApplication run时,会prepareEnvironment, 并广播 ApplicationEnvironmentPreparedEvent事件
  3. BootstrapApplicationListener 监听到事件,进行bootstrapServiceContext启动bootstrap上下文, bootstrapServiceContext后addAncestorInitializer 添加初始化器给主应用上下文
  4. 主SpringApplication run方法内,调用prepareContext准备上下文方法时会调用applyInitializers应用初始化器
  5. 第3步添加的初始化器,org.springframework.cloud.bootstrap.BootstrapApplicationListener.AncestorInitializer(bootstrap context存在AncestorInitializer的parent中)会将bootstrap context设为main context的parent

springcloud bootstrap context加载过程解析以及怎么成为main context的parent相关推荐

  1. Linux启动加载过程解析

    启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬 ...

  2. 类的加载过程详解:加载、验证、准备、解析、初始化

    想要弄明白的知识点: 类加载的过程,加载.验证.准备.解析.初始化.每个部分详细描述. 加载阶段读入.class文件,class文件时二进制吗,为什么需要使用二进制的方式? 验证过程是防止什么问题?验 ...

  3. Trembling ! Java类的加载过程详解(加载验证准备解析初始化使用卸载)

    [1]类的生命周期 一个类从加载进内存到卸载出内存为止,一共经历7个阶段: 加载->验证->准备->解析->初始化->使用->卸载 其中,类加载包括5个阶段: 加载 ...

  4. 浏览器加载、解析、渲染的过程

    最近在学习性能优化,学习了雅虎军规 ,可是觉着有点云里雾里的,因为里面有些东西虽然自己也一直在使用,但是感觉不太明白所以然,比如减少DNS查询,css和js文件的顺序.所以就花了时间去了解浏览器的工作 ...

  5. linux优化网页加载过程,HTML页面加载和解析流程 介绍

    1.浏览器加载和渲染html的顺序 1.1.IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的. 1.2.在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相 ...

  6. linux查看进程加载了哪些dll,linux下动态链接库的加载及解析过程

    http://hi.baidu.com/hust_chen/blog/item/54a8c516231d0c0ec93d6d3e.html linux下动态链接库的加载及解析过程(ZZ) 2008-1 ...

  7. spring bean加载过程_Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  8. Tomcat7.0源码分析——server.xml文件的加载与解析

    前言 作为Java程序员,对于Tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载和解析进行分析. 加载过程分析 Bootst ...

  9. 看看Spring的源码(一)——Bean加载过程

    首先Web项目使用Spring是通过在web.xml里面配置 org.springframework.web.context.ContextLoaderListener初始化IOC容器的. <l ...

最新文章

  1. Linux下截取指定时间段日志并输出到指定文件
  2. PAT 1025 反转链表 (25)(STL-map+思路+测试点分析)
  3. vue 版本发布 在线跟新用户操作解决方案_Vue3.0正式发布,本次发布所有总结,一起看看!【附在线视频】...
  4. 随机数是真是假你说了算???
  5. Stern-Brocot Tree
  6. 国外知名的开源项目托管网站
  7. Quest Recovery Manager for Active Directory的使用(一)
  8. 使用mvc时,在视图view中使用强类型视图,在web.config文件中添加命名空间namespace的引用不起作用,解决方法...
  9. android audio 自动播放,HTML5之audio无法自动播放的问题
  10. 6.6使用环境变量配置外部环境
  11. java集合框架中迭代器的作用_Java中的集合框架之迭代器
  12. 51单片机DS1302时钟
  13. 关于web的重定向,js实现重定向的方法
  14. 台式计算机如何设置无线网络,台式电脑怎么设置无线网络
  15. cpu和显卡瓶颈测试软件,CPU与GPU瓶颈的详述
  16. 【工控老马】OPC通讯协议解析-OPC七问
  17. 复数Complex类
  18. 交付管理——怎样与客户打交道
  19. 广告传媒实际税负怎么计算_文化传媒 广告行业企业怎么来合法节税,税收案例展示...
  20. 西门子PLC S7-1200如何实现远程上下载?

热门文章

  1. Neo4j:足球转移图表
  2. 【设计模式】策略模式
  3. 翻译官方Vellum教程:Breaking and tearing(破裂撕开)
  4. 【makefile】wildcard函数
  5. Eclipse/Myeclipse自定义JSP模板
  6. 二层和三层、四层交换机工作原理
  7. 《人类简史:从动物到上帝》读书活动策划
  8. Bresenham画圆 正负画圆法 中点画圆法
  9. 工作过程中积累的书签(链接)
  10. 蓝天白云青山绿水还有清风吹斜阳......