在上篇文章:SpringBoot源码解析:创建SpringApplication对象实例中,我们详细描述了SpringApplication对象实例的创建过程,本篇文章继续看run方法的执行逻辑吧

	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);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//后面还有,本篇文章就解析到这。。。。
}
  1. 第一行使用了StopWatch来记录开始时间
  2. 设置了java.awt.headless环境变量,在网上了解了一下这个变量的相关信息

Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式

个人理解为是一些图形相关的组件能否使用的开关,欢迎各位大佬指正

  1. 接着遍历所有构造SpringApplication实例时加载的SpringApplicationRunListener,调用它们的started方法

这里构造时仅仅加载了一个EventPublishingRunListener类,所以咱们就来解析一下这个东东

    public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}

可以看到这里调用了SimpleApplicationEventMulticaster类的multicastEvent方法并且传入了ApplicationStartingEvent对象,看名字就知道了这个是SpringBoot启动事件


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

其中获取监听器使用的是getApplicationListeners方法,这个方法中主要就是从最启动时获取的所有监听器和这个事件做了下匹配,返回通过匹配的监听器集合

接着就是看是否设置线程池参数,如果有线程池则使用线程池的线程进行操作,否则将同步调用监听器

  1. 把所有的命令行启动参数封装成ConfigurableEnvironment对象
  2. 准备运行时环境
	private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
获取或创建环境getOrCreateEnvironment

方法名就很直观,有就直接获取,没有就新建

	private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}

上篇文章中说过了,咱们是Servlet环境,所以当前方法是返回一个StandardServletEnvironment对象,这个对象的构造过程中调用了customizePropertySources方法(它父类的父类调用的)

	protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
propertySources.addLast(new StubPropertySource("servletContextInitParams"));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource("jndiProperties"));
}
super.customizePropertySources(propertySources);
}
//这是它父类的
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment()));
}

可以看出StandardServletEnvironmentpropertySources中添加了两个StubPropertySource对象,而它的父类添加了一个包含java系统属性和一个操作系统环境变量的对象

配置 configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
// 配置PropertySources
configurePropertySources(environment, args);
// 配置Profiles
configureProfiles(environment, args);
}

分别看一下两个方法

配置PropertySources
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
// 存在默认配置将其放到最后位置
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 如果存在命令行参数则将原有的替换掉
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
"springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
// 将其放到第一位置
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}

这里就体现出了这个命令行参数比应用配置文件的优先级高的情况了

配置Profiles

从PropertySources中查找spring.profiles.active属性,存在则将其值添加activeProfiles集合中

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles();
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
发布EnvirongmentPreparedEvent事件
绑定环境
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
转换环境

如果web环境变更为NONE则将StandardServletEnvironment转换为StandardEnvironment

ConfigurationPropertySources.attach(environment)
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment)
.getPropertySources();
PropertySource<?> attached = sources.get("configurationProperties");
if (attached != null && attached.getSource() != sources) {
sources.remove("configurationProperties");
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(
"configurationProperties",
new SpringConfigurationPropertySources(sources)));
}
}

最终这个sources对象的第一个位置放的是它自己,循环引用,这个具体的含义还有待挖掘

SpringApplication到底run了什么(上)的更多相关文章

  1. SpringApplication到底run了什么(下)

    在上篇文章中SpringApplication到底run了什么(上)中,我们分析了下面这个run方法的前半部分,本篇文章继续开工 public ConfigurableApplicationConte ...

  2. SpringBoot启动流程分析(二):SpringApplication的run方法

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

  3. SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法

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

  4. Springcloud Eureka 启动失败:ERROR org.springframework.boot.SpringApplication - Application run failed

    在测试Euruka作为服务注册中心的时候碰到了这个问题 [main] ERROR org.springframework.boot.SpringApplication - Application ru ...

  5. 基于Redis的分布式锁到底安全吗(上)?

    基于Redis的分布式锁到底安全吗(上)?  2017-02-11 网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词“Redis 分布式锁”随便到哪个搜索引擎上去搜索一下就知道了 ...

  6. SpringBoot源码解析系列文章汇总

    相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的SpringBoot源码解析系列文章的汇总,当你使用SpringBoot不仅仅满足于基本使用时.或者出去面试被面试官虐了时.或者说想要深入了解一下 ...

  7. Spring boot 源码分析(一)SpringApplication.run(上)

    SpringApplication.run(Main.class, args); 从这个方法开始讲吧: public static ConfigurableApplicationContext run ...

  8. springboot启动流程(二)SpringApplication run方法核心逻辑

    所有文章 https://www.cnblogs.com/lay2017/p/11478237.html run方法逻辑 在上一篇文章中,我们看到SpringApplication的静态方法最终是去构 ...

  9. 011-Spring Boot 运行流程分析SpringApplication.run

    一.程序入口 1.1.静态方法 //直接调用run方法 ConfigurableApplicationContext context = SpringApplication.run(App.class ...

随机推荐

  1. 1.python进行if条件相等时候的条件

    在我们进行 if == 判断的时候!其中判断的条件: 1:其值是不是一样 3:其类型是否是一样 ###二者少了任何一个都不可以 >>> pwd = 23>>> cc ...

  2. linux下tree命令产生乱码,通过修改字符集解决

    alias tree='tree --charset ASCII'

  3. 【Spring 整合篇】整合SpringMVC+Mybatis(一)

    首先,创建Maven Project,packaging为war,修改pom.xml. <properties> <!-- JDK版本 --> <java.version ...

  4. Linux下Nginx的安装(二)

    一.安装Nginx ## 安装前准备 ## #GNU编译器集合 #Nginx编译需要PCRE #在Nginx的各种模块中需要使用gzip压缩 #在Nginx中,如果服务器提供安全网页时则会用到Open ...

  5. 【使用篇二】SpringBoot整合Servlet(1)

    两种方式: 通过注解扫描完成 Servlet组件的注册 通过方法完成 Servlet组件的注册 一.通过注解扫描完成 Servlet 组件的注册 1. 编写Servlet类 /** * SpringB ...

  6. [RN] React Native 使用 Redux 比较详细和深刻的教程

    React Native 使用 Redux 比较详细和深刻的教程 React Native 使用 Redux https://www.jianshu.com/p/06fc18cef56a http:/ ...

  7. [LeetCode] 421. Maximum XOR of Two Numbers in an Array 数组中异或值最大的两个数字

    Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231. Find the maximum re ...

  8. [LeetCode] 417. Pacific Atlantic Water Flow 太平洋大西洋水流

    Given an m x n matrix of non-negative integers representing the height of each unit cell in a contin ...

  9. Spring Cloud Gateway 之 AddRequestHeader GatewayFilter Factory

    今天我们来学习下GatewayFilter Factory,中文解释就是过滤器工厂. 官方文档对GatewayFilter Factory的介绍: Route filters allow the mo ...

  10. Mac:输出phpinfo的信息

    输出phpinfo的信息1.echo '<?php phpinfo(); ?>' | php 2>&12.php -a 进入Interactive shell模式然后:pri ...