In the previous article: SpringBoot Source Parsing: Create a SpringApplication Object Instance In this article, we will continue to look at the execution logic of the run method.
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); //Later on, this article will resolve to... }
- The first line uses StopWatch to record the start time
-
Set the java.awt.headless environment variable to learn about it online
Headless mode is a configuration mode for the system.You can use this mode if your system may lack peripherals such as a display device, keyboard, or mouse
Personally, I think it is a switch that can be used by some graphics-related components. You guys are welcome to make corrections.
Then iterate through all the SpringApplicationRunListener s that were loaded when the SpringApplication instance was constructed, calling their started method
The construction here loads only one EventPublishingRunListener class, so let's parse this East
public void starting() { this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); }
You can see here that the multicastEvent method of the SimpleApplicationEventMulticaster class is called and the ApplicationStartingEvent object is passed in. You can see by name that this is a SpringBoot startup event
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); } } }
Getting listeners uses the getApplicationListeners method, which essentially matches all listeners acquired at startup with this event and returns the set of listeners through matching
The next step is to see if the thread pool parameter is set or if there is a thread pool, the thread from the thread pool will be used to operate, otherwise the listener will be called synchronously
- Encapsulate all command line startup parameters into a Configurable Environment object
- Prepare Runtime Environment
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; }
Get or create environment getOrCreateEnvironment
Method names are intuitive, get them directly, create new ones without them
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webApplicationType == WebApplicationType.SERVLET) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }
As mentioned in the previous article, we are a Servlet environment, so the current method is to return a Standard Servlet Environment object that was constructed using the customizePropertySources method (called by its parent class)
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); } //This is its parent protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment())); }
You can see that StandardServletEnvironment adds two StubPropertySource objects to propertySources, and its parent adds an object containing a java system property and an operating system environment variable
Configure configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { // Configure PropertySources configurePropertySources(environment, args); // Configure Profiles configureProfiles(environment, args); }
Look at the two methods separately
Configure PropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { // There is a default configuration to put it last sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } // Replace command line parameters if they exist 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 { // Put it in the first place sources.addFirst(new SimpleCommandLinePropertySource(args)); } } }
This reflects the fact that this command line parameter has a higher priority than the application configuration file
Configure Profiles
Find the spring.profiles.active property from PropertySources and add its value to the activeProfiles collection if it exists
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)); }
Publish EnvirongmentPreparedEvent Event Event
Binding environment
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); } }
Conversion environment
Convert Standard ServletEnvironment to Standard Environment if the web environment changes to NONE
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))); } }
Ultimately, the first position of this sources object is itself, a circular reference, which has yet to be explored.