在上篇文章: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); //後面還有,本篇文章就解析到這。。。。 }
StopWatch
來記錄開始時間java.awt.headless
環境變量,在網上了解了一下這個變量的相關信息Headless模式是系統的一種配置模式。在系統可能缺乏顯示設備、鍵盤或鼠標這些外設的狀況下可使用該模式web
我的理解爲是一些圖形相關的組件可否使用的開關,歡迎各位大佬指正spring
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
接着就是看是否設置線程池參數,若是有線程池則使用線程池的線程進行操做,不然將同步調用監聽器操作系統
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系統屬性和一個操做系統環境變量的對象code
configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { // 配置PropertySources configurePropertySources(environment, args); // 配置Profiles configureProfiles(environment, args); }
分別看一下兩個方法
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)); } } }
這裏就體現出了這個命令行參數比應用配置文件的優先級高的狀況了
從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
對象的第一個位置放的是它本身,循環引用,這個具體的含義還有待挖掘