What exactly is SpringApplication run ning?

Keywords: Java SpringBoot Spring

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...
    }
  1. The first line uses StopWatch to record the start time
  2. 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.

  3. 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

  1. Encapsulate all command line startup parameters into a Configurable Environment object
  2. 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.

Posted by akuji36 on Mon, 23 Sep 2019 09:45:50 -0700