SpringApplication到底run了什麼(上)

在上篇文章:SpringBoot源碼解析:建立SpringApplication對象實例中,咱們詳細描述了SpringApplication對象實例的建立過程,本篇文章繼續看run方法的執行邏輯吧java

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模式是系統的一種配置模式。在系統可能缺乏顯示設備、鍵盤或鼠標這些外設的狀況下可使用該模式web

我的理解爲是一些圖形相關的組件可否使用的開關,歡迎各位大佬指正spring

  1. 接着遍歷全部構造SpringApplication實例時加載的SpringApplicationRunListener,調用它們的started方法

這裏構造時僅僅加載了一個EventPublishingRunListener類,因此我們就來解析一下這個東東app

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

能夠看到這裏調用了SimpleApplicationEventMulticaster類的multicastEvent方法而且傳入了ApplicationStartingEvent對象,看名字就知道了這個是SpringBoot啓動事件less

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方法,這個方法中主要就是從最啓動時獲取的全部監聽器和這個事件作了下匹配,返回經過匹配的監聽器集合this

接着就是看是否設置線程池參數,若是有線程池則使用線程池的線程進行操做,不然將同步調用監聽器操作系統

  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系統屬性和一個操做系統環境變量的對象code

配置 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對象的第一個位置放的是它本身,循環引用,這個具體的含義還有待挖掘

1 1

相關文章
相關標籤/搜索