在上篇文章: "SpringBoot源碼解析:創建SpringApplication對象實例" 中,我們詳細描述了SpringApplication對象實例的創建過程,本篇文章繼續看 方法的執行邏輯吧 1. 第一行使用了 來記錄開始時間 2. 設置了 環境變數,在網上瞭解了一下這個變數的相關信息 H ...
在上篇文章: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);
//後面還有,本篇文章就解析到這。。。。
}
- 第一行使用了
StopWatch
來記錄開始時間 設置了
java.awt.headless
環境變數,在網上瞭解了一下這個變數的相關信息Headless模式是系統的一種配置模式。在系統可能缺少顯示設備、鍵盤或滑鼠這些外設的情況下可以使用該模式
個人理解為是一些圖形相關的組件能否使用的開關,歡迎各位大佬指正
接著遍歷所有構造
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
方法,這個方法中主要就是從最啟動時獲取的所有監聽器和這個事件做了下匹配,返回通過匹配的監聽器集合
接著就是看是否設置線程池參數,如果有線程池則使用線程池的線程進行操作,否則將同步調用監聽器
- 把所有的命令行啟動參數封裝成
ConfigurableEnvironment
對象 - 準備運行時環境
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()));
}
可以看出StandardServletEnvironment
往propertySources
中添加了兩個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
對象的第一個位置放的是它自己,迴圈引用,這個具體的含義還有待挖掘