SpringCloud这个框架本身是建立在SpringBoot基础之上的,所以使用SpringCloud的方式与SpringBoot相仿。也是通过类似如下代码进行启动。

SpringApplication.run(XxxApplication.class, args);

其中 XxxApplication.class 类上也需要添加 @SpringBootApplication注解。

要使用SpringCloud框架,在pom文件中要确保引入  spring-cloud-starter 依赖包, spring-cloud-starter 依赖如下的 jar :

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
</dependencies>

其中  spring-cloud-context-x.y.z.RELEASE.jar 和 spring-cloud-commons-x.y.z.RELEASE.jar 下的 META-INF 目录下都包含  spring.factories 文件,所以可以把这两个jar看作是springCloud程序的入口。

SpringCloud在构建上下文 (即ApplicationContext实例)时,采用了Spring父子容器的设计,会在 SpringBoot构建的容器(后面称之为应用容器)之上创建一父容器 Bootstrap Application Context .

那么SpringCloud设计出Bootstrap Application Context ,并把它作为 应用容器的父容器的目的是什么呢:

因为SpringCloud 作为一个微服务框架,需要使用全局的配置中心,而配置中心的配置是可以提供给应用容器的,所以在应用容器初始化和实例化Bean之前需要先完成配置中心的实例化,这个任务就由Bootstrap Application Context 来完成,而配置中心的相关配置属性就从bootstrap.properties或bootstrap.yml文件中读取。

但要注意的是,在Bootstrap Application Context 启动工作完成之后,其从bootstrap.properties或bootstrap.yml文件中读取的配置,是会被应用容器对应的application.properties或yml文件中的同名属性覆盖的。

下面从源码角度来分析上面的论述:

1. 代码运行时还是从SpringApplication实例的run方法开始 ,此处会触发 BootstrapApplicationListener 类中的代码 , Bootstrap Application Context 的创建就是通过这个监听器触发的

public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//此处会加载spring-cloud-context提供的监听器org.springframework.cloud.bootstrap.BootstrapApplicationListener.class
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//此处会发布ApplicationEnvironmentPreparedEvent事件,触发BootstrapApplicationListener中的代码
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
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;
}

2. Bootstrap Application Context 的实例化 ,由BootstrapApplicationListener类的 onApplicationEvent方法触发

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
//可以通过环境变量 spring.cloud.bootstrap.enabled来禁止使用Bootstrap容器
if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
true)) {
return;
}
// 由于Bootstrap容器在创建时还是会再次调用上面步骤1的代码,还会再次触发BootstrapApplicationListener类这个方法,所以此处作个判断,
          如果当前是Bootstrap容器的处理,则直接返回
if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
return;
}
ConfigurableApplicationContext context = null;
//获取配置文件的名字,默认为bootstrap.properties或.yml ,并且这个名字可以通过 spring.cloud.bootstrap.name在环境中配置
String configName = environment
.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
.getInitializers()) {
//从相应jar中的spring.factories文件中读取初始化器的配置类实例,如果这个实例类是ParentContextApplicationContextInitializer类型
则直接从该类中获取到父容器,默认情况下,没有提供这样的类,下面这段代码会跳过
if (initializer instanceof ParentContextApplicationContextInitializer) {
context = findBootstrapContext(
(ParentContextApplicationContextInitializer) initializer,
configName);
}
}
if (context == null) {
//此处分析见步骤3
context = bootstrapServiceContext(environment, event.getSpringApplication(),
configName);
event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
} apply(context, event.getSpringApplication(), environment);
}

3. bootstrap容器的创建

private ConfigurableApplicationContext bootstrapServiceContext(
ConfigurableEnvironment environment, final SpringApplication application,
String configName) {
/**
此处代码主要是从各处获取属性配置,此处忽略
**/

// 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); //SpringApplicationBuilder的作用:1.构建SpringApplication 2.构建ApplicationContext
//这里需要思考一下,springboot在启动时已经构建了一个SpringApplication实例,为何此处又构建了一个
//这是因为这个SpringApplication实例的构建环境和SringBoot原生构建的那个不同,看一下上一行代码就能明白
final SpringApplication builderApplication = builder.application();
if(builderApplication.getMainApplicationClass() == null){
builder.main(application.getMainApplicationClass());
}
if (environment.getPropertySources().contains("refreshArgs")) {
builderApplication
.setListeners(filterListeners(builderApplication.getListeners()));
}
//从springFactories文件中查找BootstrapConfiguration的配置类
builder.sources(BootstrapImportSelectorConfiguration.class); //构建出BootstrapContext
final ConfigurableApplicationContext context = builder.run();
context.setId("bootstrap");
// 设置BootstrapContext成为应用Context的父容器,此处分析见步骤4
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;
}

4. 设置BootstrapContext成为应用Context的父容器 (在SpringApplication实例中动态添加了一个初始化器,相当于给应用Context埋了个雷)

private void addAncestorInitializer(SpringApplication application,
ConfigurableApplicationContext context) {
boolean installed = false;
//从spring.factories文件中获取初始化器的配置类且类型为AncestorInitializer
for (ApplicationContextInitializer<?> initializer : application
.getInitializers()) {
if (initializer instanceof AncestorInitializer) {
installed = true;
// New parent
((AncestorInitializer) initializer).setParent(context);
}
}
//默认情况下是没有配围置AncestorInitializer这样的类,此处是则执行由BootstrapListener提供的内部类
if (!installed) {
//将BootstrapContext作为父容器传到AncestorInitializer实例中,并将其放入SpringApplication实例的初始器列表中
application.addInitializers(new AncestorInitializer(context));
} }

5. 初始化器AncestorInitializer被触发,是由应用Context的处理触发的

public void initialize(ConfigurableApplicationContext context) {  //这个context是应用Context
while (context.getParent() != null && context.getParent() != context) {
context = (ConfigurableApplicationContext) context.getParent();
}
reorderSources(context.getEnvironment());
//完成应用容器的父容器的设置
new ParentContextApplicationContextInitializer(this.parent)
.initialize(context);
}

6. ParentContextApplicationContextInitializer代码

private static class ParentContextApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> { private final ApplicationContext parent; ParentContextApplicationContextInitializer(ApplicationContext parent) {
this.parent = parent;
} @Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.setParent(this.parent); //设置应用Context的父容器
} }

总结:

  1. SpringCloud Context模块的功能  :主要是构建了一个Bootstrap容器,并让其成为原有的springboot程序构建的容器的父容器。

  2. Bootstrap容器的作用:是为了预先完成一些bean的实例化工作,可以把Bootstrap容器看作是先头部队。

  3. Bootstrap容器的构建:是利用了Springboot的事件机制,当 springboot 的初始化 Environment  准备好之后会发布一个事件,这个事件的监听器将负责完成Bootstrap容器的创建。构建时是使用 SpringApplicationBuilder 类来完成的。

4. 如何让Bootstrap容器与应用Context 建立父子关系 :由于Bootstrap容器与应用Context都是关联着同一个SpringApplication实例,Bootstrap容器自己完成初始化器的调用之后,会动态添加了一个初始化器 AncestorInitializer,相当于给应用Context 埋了个雷 ,这个初始化器在应用容器进行初始化器调用执行时,完成父子关系的设置。

Spring Cloud Context模块的更多相关文章

  1. Spring Cloud Commons模块

    上一篇介绍了 Spring Cloud Context模块 ,本文介绍SpringCloud的另一个基础模块 SpringCloud Commons模块 .只要在项目的pom文件中引入了spring- ...

  2. 【spring cloud】【IDEA】【Maven】spring cloud多模块打包,打包的jar包只有几k,jar包无法运行,运行报错:no main manifest attribute, in /ms-eureka.jar

    ======================================================================================== 引申:maven打包多 ...

  3. 【spring cloud】【IDEA】【maven】spring cloud多模块在idea上使用maven插件打包报错:程序包XXX不存在

    >>>>spring cloud 多模块 >>>>在idea上使用maven插件打包,欲打包成jar包后 进行部署 >>>> 报 ...

  4. SpringCloud入门之应用程序上下文服务(Spring Cloud Context)详解

    构建分布式系统非常复杂且容易出错.Spring Cloud为最常见的分布式系统模式提供了简单易用的编程模型,帮助开发人员构建弹性,可靠和协调的应用程序.Spring Cloud构建于Spring Bo ...

  5. Spring cloud基础模块学习

    1.微服务架构构成 单体架构(通过应用集群和数据库集群来提高性能,多余模块存在浪费) 垂直架构(新的功能模块通过新项目来实现,数据库之间存在交叉关联.存在数据冗余,和单体架构一样通过扩展集群结点,成本 ...

  6. Spring cloud公共模块

    1.0公共的模块是公共的工具包以及实体等 2.添加架包 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs ...

  7. spring cloud各个模块作用

    Eureka Client:负责将这个服务的信息注册到Eureka Server中.Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号.ribbon:负载均衡,获 ...

  8. spring cloud连载第一篇之bootstrap context

    1. Spring Cloud Context: Application Context Services(应用上下文服务) 1.1 The Bootstrap Application Context ...

  9. 利用 Maven 构造 Spring Cloud 微服务架构 模块使用 spring Boot构建

    采用Maven 聚合工程搭建 Spring Cloud 使用工具: IntelliJ IDEA    版本: 2019.2.2 maven             版本: 3.6.0 JDK      ...

随机推荐

  1. centos搭建ftp服务器

    申请个京东云服务用着.上传文件想搭建个ftp服务.遇到个坑记录一下: 这里就简单的使用yum安装 ftp服务: vsftpd  全称 very secure ftp deamon  (非常安全的ftp ...

  2. 记一次Maven编译IKAnalyzer失败及解决办法

    下载了一个开源项目,maven形式组织的,其中有一个依赖包是IKAnalyzer. 由于mvnrepository中不存在IKAnalyzer的坐标,因此该依赖包需要自己下载安装到本地maven仓库才 ...

  3. UIImagePickerController - 官方文档说明

    使用UIImagePickerController对象的步骤: 1)验证设备是否能从目标源获取内容,通过调用 + (BOOL)isSourceTypeAvailable:(UIImagePickerC ...

  4. Bootstrap开发框架界面的调整处理

    我在之前介绍了很多关于Boostrap的框架方面的文章,主要是介绍各种插件的使用居多,不过有时候觉得基于Metronic的Boostrap框架的界面效果不够紧凑,希望对它进行一定的调整,那么我们应该如 ...

  5. [转帖]Scanners-Box 指引

    作者:杨文链接:https://zhuanlan.zhihu.com/p/26534414来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 原文地址:We5ter/Sca ...

  6. vue.js实战——splice使用

    Vue在检测到数组变化时,并不是直接重新渲染整个列表,而是最大化地复用DOM元素.替换的数组中含有相同元素的项不会被重新渲染,因此可以大胆地用新数组来替换就数组,不用担心性能问题. 需要注意的是,以下 ...

  7. vue服务器端渲染

    Vue.js 是构建客户端应用程序的框架.默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM.然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏 ...

  8. java 11 完全支持Linux容器(包括Docker)

    许多运行在Java虚拟机中的应用程序(包括Apache Spark和Kafka等数据服务以及传统的企业应用程序)都可以在Docker容器中运行.但是在Docker容器中运行Java应用程序一直存在一个 ...

  9. java 11 标准Java异步HTTP客户端

    这是 Java 9 开始引入的一个处理 HTTP 请求的的 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到 ...

  10. Spring security oauth2 client_credentials认证 最简单示例代码

    基于spring-boot-2.0.0 1,在pom.xml中添加: <!-- security --> <!-- https://mvnrepository.com/artifac ...