在org.springframework.context.ApplicationListener众多实现者有一个非常重要的实现就是
org.springframework.cloud.bootstrap.BootstrapApplicationListener
可以看到,ApplicationListener是定义在spring-context包中,属于经典上下文范畴,BootstrapApplicationListener是在spring-cloud-context中,属于cloud上线文范畴。

在classpath中出现spring-cloud-context.jar包时,它在spring.factories文件里写了BootstrapApplicationListener,所以在构建SpringApplication对象时就扫描出这个监听器了。然后在run()方法中发送事件给监听器,监听器执行回调。

官方解释:
这个listener会prepare SpringApplication(例如填充他的Environment等),通过代理给在独立的bootstrap context里的ApplicationContextInitializer beans。bootstrap context 是SpringApplication从spring.factories中BootstrapConfiguration中创建的,从bootstrap.properties(or yml)而不是普通的application.properties(or yml)初始化的。

BootstrapApplicationListener执行后会创建一个id=bootstrap的上下文,这个上下文不是一个独立的父上下文,不是装载应用bean的上下文。

public class BootstrapApplicationListenerimplements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {@Overridepublic 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) {context = bootstrapServiceContext(environment, event.getSpringApplication(),configName);event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));}apply(context, event.getSpringApplication(), environment);}}

整个回调方法通过开关控制

spring.cloud.bootstrap.enabled

org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent()方法做的事情是

其中核心的方法是:
org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext()
这个方法会创建一个bootstrapEnvironment,一个bootstrapApplicationContext,这个applicationContext可以认为是一个父上下文。

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:}");String configAdditionalLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.additional-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);}if (StringUtils.hasText(configAdditionalLocation)) {bootstrapMap.put("spring.config.additional-location",configAdditionalLocation);}bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));for (PropertySource<?> source : environment.getPropertySources()) {if (source instanceof StubPropertySource) {continue;}bootstrapProperties.addLast(source);}// 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()));}builder.sources(BootstrapImportSelectorConfiguration.class);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 contextaddAncestorInitializer(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;
}

比较关键的点是这里手动引入了一个导入配置类:
org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration

@Configuration(proxyBeanMethods = false)
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {}

进而导入:
org.springframework.cloud.bootstrap.BootstrapImportSelector
BootstrapImportSelector的主要作用就是从spring.factories文件中读取:
org.springframework.cloud.bootstrap.BootstrapConfiguration配置的类

public class BootstrapImportSelector implements EnvironmentAware, DeferredImportSelector {private Environment environment;private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicatesList<String> names = new ArrayList<>(SpringFactoriesLoader.loadFactoryNames(BootstrapConfiguration.class, classLoader));names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(this.environment.getProperty("spring.cloud.bootstrap.sources", ""))));List<OrderedAnnotatedElement> elements = new ArrayList<>();for (String name : names) {try {elements.add(new OrderedAnnotatedElement(this.metadataReaderFactory, name));}catch (IOException e) {continue;}}AnnotationAwareOrderComparator.sort(elements);String[] classNames = elements.stream().map(e -> e.name).toArray(String[]::new);return classNames;}class OrderedAnnotatedElement implements AnnotatedElement {private final String name;private Order order = null;private Integer value;OrderedAnnotatedElement(MetadataReaderFactory metadataReaderFactory, String name)throws IOException {MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(name);AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();Map<String, Object> attributes = metadata.getAnnotationAttributes(Order.class.getName());this.name = name;if (attributes != null && attributes.containsKey("value")) {this.value = (Integer) attributes.get("value");this.order = new Order() {@Overridepublic Class<? extends Annotation> annotationType() {return Order.class;}@Overridepublic int value() {return OrderedAnnotatedElement.this.value;}};}}@Override@SuppressWarnings("unchecked")public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {if (annotationClass == Order.class) {return (T) this.order;}return null;}@Overridepublic Annotation[] getAnnotations() {return this.order == null ? new Annotation[0]: new Annotation[] { this.order };}@Overridepublic Annotation[] getDeclaredAnnotations() {return getAnnotations();}@Overridepublic String toString() {return new ToStringCreator(this).append("name", this.name).append("value", this.value).toString();}}}
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.cloud.util.random.CachedRandomPropertySourceAutoConfiguration

Spring Cloud启动-4-应用监听器ApplicationListener之BootstrapApplicationListener相关推荐

  1. Spring Boot 启动事件和监听器,太强大了!

    大家都知道,在 Spring 框架中事件和监听无处不在,打通了 Spring 框架的任督二脉,事件和监听也是 Spring 框架必学的核心知识之一. 一般来说,我们很少会使用到应用程序事件,但我们也不 ...

  2. spring cloud 启动bean 循环依赖问题记录

    今天在搭建spring cloud 的过程中遇到了bean 循环依赖的问题,所以记录一下. spring-boot:2.0.1 spring-cloud:2.0.1 标记的地方出现了循环.导致启动不成 ...

  3. Spring Cloud 启动Eureka的Client(客户端)时,项目一启动就停止,控制台无任何报错信息

    问题描述:再利用IDEA开发工具快速构建一个Spring Cloud中Eureka 客户端(client)后,项目一点击启动就停止,控制台无任何报错信息. 解决方法: 当出现这种情况的时候我们先在控制 ...

  4. Spring Cloud启动慢Initializing ExecutorService 'taskScheduler'占用三分钟

    Spring Cloud: 1.问题: 工作例子,启动时,用了三分钟多,才启动成功,查看日志时间 2019-08-23 10:11:20,702 test INFO  o.s.scheduling.c ...

  5. Spring Cloud系列之Commons - 1. 背景与基础知识准备

    本文基于 Spring Cloud 2020.0 发布版的依赖 本系列会深入分析 Spring Cloud 的每一个组件,从Spring Cloud Commons这个 Spring Cloud 所有 ...

  6. Spring Cloud 2020.0.0 正式发布,对开发者来说意味着什么?

    作者 | YourBatman 在线教育领域资深架构师,Spring Framework 开源贡献者 冷冷 云集架构师.开源项目 pig 负责人.Spring Cloud Alibaba Commit ...

  7. Spring Cloud –基本设置

    Spring Cloud解决了分布式系统的常见问题. 但是,对于只使用广为人知的整体应用程序工作的人来说,从一开始就跳入一长串为分布式服务设计的模式可能会让人不知所措. 本文将通过实用的方法为您介绍S ...

  8. springcloud项目的启动顺序_spring boot/cloud 启动方式说明

    spring boot/cloudsh的java开源框架,spring cloud更多注重服务注册以及服务治理,通俗来讲就是我们所说的微服务,需要注意的是spring cloud是基于spring b ...

  9. 强大的Spring Boot启动监听器事件-初始化系统账号密码

    文章目录 前言 一.SpringApplicationEvents 事件类型 1.1 ApplicationStartingEvent 1.2 ApplicationEnvironmentPrepar ...

最新文章

  1. Java核心技术卷I基础知识3.6.6 码点与代码单元
  2. Noip2016换教室
  3. 第 21 章 —— 单例模式
  4. linux内核关于io的变迁
  5. SAP query生成的程序名
  6. Eclipse 中切换不同的JDK设置
  7. 深入理解 JVM Class文件格式(三)
  8. ug弹簧可变性装配_弹簧可配置魔术
  9. ANTLR和网络:一个简单的例子
  10. 使用JavaScript进行数组去重——一种高效的算法
  11. 全排列及相关扩展算法(六)——全排列最蛋疼的算法:邻位对换法
  12. logistic regression一点理解
  13. 利用linux curl爬取网站数据
  14. CnBlogs博文demo演示技巧比较:jsfiddle完胜
  15. 交通流理论3——交通流三大参数
  16. ibase4j使用信息心得
  17. 大数据技术与实践实验报告总结_大数据平台搭建实验心得体会
  18. 想学文字生成图片?3招告诉你描述文字生成图片怎么做
  19. oracle使用(五)表空间创建、删除以及删除后数据文件还存在的问题
  20. 平面设计工作的8个基本技能

热门文章

  1. 三招教你如何搞定将qlv格式的腾讯视频转换为mp4格式
  2. android串口开发!一年后斩获腾讯T3,附面试题答案
  3. 人生就像四季,繁华过后终归平淡,不可能一直繁华似锦,不可能一路花香四溢。...
  4. 【技法操作】UI界面设计,用PS绘制定位app页面设计教程
  5. 二层交换机、三层交换机及四层交换机的区别
  6. 关于前端职业规划的一点思考
  7. 优化 | 浅谈旅行商(TSP)的七种整数规划模型
  8. mac shell 清理缓存-如微信mac清理微信缓存
  9. Client-Initiated场景下的L2TP实验配置
  10. (原创推荐文章)kerberos服务器端与客户端