在上篇文章: 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
个人理解为是一些图形相关的组件能否使用的开关,欢迎各位大佬指正
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); }
分别看一下两个方法
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
对象的第一个位置放的是它自己,循环引用,这个具体的含义还有待挖掘