SpringBoot - Creation of SpringApplication Object for Startup Principle

Create SpringApplication Object

SpringBoot version 2.1.1.RELEASE

@SpringBootApplication
public class SpringbootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }

This is the startup class for the SpringBoot project. Within the main method, the static run() method of the SpringApplication class is called, and the byte code of the startup class and the parameters of the main method are included as functions.

We run the code as a Debug, and we can see that the SpringBoot project creates the SpringApplication object first when it starts.The code is as follows:

public static ConfigurableApplicationContext run(Class<?> primarySource,
            String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }


public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
        return new SpringApplication(primarySources).run(args);
    }


public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }


public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

You can see that when you create a SpringApplcation object, you invoke the construction method of the SpringApplication class with the Class class of the startup class as an input.

The following highlights this construction method:

A: this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
This line of code stores the startup class in the primarySources property of the SpringApplication object
B: this.webApplicationType = WebApplicationType.deduceFromClasspath();
This method is to infer the type of Web application

static WebApplicationType deduceFromClasspath() {
        //Inference to WebFlux type when DispatcherHander exists but there is no DispatcherServlet
        
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        // When neither Servlet nor Configurable WebApplicationContext exists, infer to be a non-Web application
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

Since we only introduced web dependencies, we returned to Servlet

C: setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));

This method is used to load the Spring application context initializer.

We debug into getSpringFactories Instances ():

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

In fact, this method is to find all the ApplicationContextInitializer s from the META-INF/spring.factories file under the class path through the loadFactoryNames() method of the SpringFactoriesLoader class and save them to the initializers property of the SpringApplication s.

In the figure below, we can see that six initializers are saved in the initializers property through the getSpringFactoriesInstances() method

D: setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
Similar to the previous method, except that this method loads the Spring application event listener, but works the same way, using the loadFactoryNames() method of the SpringFactoriesLoader class, except that the initialized object becomes an ApplicationListener

E: this.mainApplicationClass = deduceMainApplicationClass();
The purpose of this method is to find the main configuration class containing the main method from multiple configuration classes

In fact, this method is not designed precisely because in the SpringApplication.run(XXX.class) method, this class object must pass in its main Configuration class, because we know that the @SpringBootApplication meta-label @EnableAutoConfiguration works essentially the same, in other words, we can pass in any class object labeled @EnableAutoConfiguration, @Configuration two annotated classes.

The next article will explain how the run() method works in SpringApplicaiton.

Keywords: Java SpringBoot Spring WebFlux

Added by Telemachus on Wed, 22 May 2019 21:35:59 +0300