前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringApplication到底run了什么(上)

SpringApplication到底run了什么(上)

作者头像
Java学习录
发布2019-09-25 14:09:59
5940
发布2019-09-25 14:09:59
举报
文章被收录于专栏:Java学习录Java学习录

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

代码语言:javascript
复制
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类,所以咱们就来解析一下这个东东

代码语言:javascript
复制
public void starting() {
    this.initialMulticaster.multicastEvent(
            new ApplicationStartingEvent(this.application, this.args));
}

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

代码语言:javascript
复制

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. 准备运行时环境
代码语言:javascript
复制
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

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

代码语言:javascript
复制
private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	if (this.webApplicationType == WebApplicationType.SERVLET) {
		return new StandardServletEnvironment();
	}
	return new StandardEnvironment();
}

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

代码语言:javascript
复制
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
代码语言:javascript
复制

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

分别看一下两个方法

配置PropertySources
代码语言:javascript
复制
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集合中

代码语言:javascript
复制
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事件
绑定环境
代码语言:javascript
复制
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)
代码语言:javascript
复制
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对象的第一个位置放的是它自己,循环引用,这个具体的含义还有待挖掘

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java学习录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 获取或创建环境getOrCreateEnvironment
  • 配置 configureEnvironment
    • 配置PropertySources
      • 配置Profiles
      • 发布EnvirongmentPreparedEvent事件
      • 绑定环境
      • 转换环境
      • ConfigurationPropertySources.attach(environment)
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档