What exactly did spring boot do?

Keywords: Programming Spring WebFlux SpringBoot Java

Preface

For server-side development, most new projects are based on spring boot. Projects using spring boot typically have this line of code

 SpringApplication.run(TestApplication.class, args);

This is where the Spring boot framework loads.It's a whim to see what it's doing.

new SpringApplication(primarySources))

The run method first creates a SpringApplicaition object, and primarySources is the first parameter of our run method, such asTestApplication.class

 public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

There are several main parts of this construction method.

WebApplicationType.deduceFromClasspath

This method is mainly used to get the type of current application, if we introduce spring-webmvc then we will return the servlet, if we use WebFlux as a reactive framework, we will return REACTIVE, which we will talk about later.

getSpringFactoriesInstances(Class classz)

The method is to find META-INF/Spring.factoriesUnder the file, all classz-type classes are then loaded into memory and the default constructor is called to generate an instance.

this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

So we know what this line of code means is instantiating META-INF/Spring.factoriesClasses of all ApplicationContextInitializer types in the file.As to what this type of class does, we'll see later. Above, all instances of ApplicationContextInitializer in springboot are created.Suppose we want to add our own ApplicationContextInitializer to write this.

@SpringBootApplication
public class MySpringBootApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MySpringBootApplication.class);
        application.addInitializers(new MyApplicationContextInitializer());
        application.run(args);
    }
}

this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

Ditto

deduceMainApplicationClass()

private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
            ;
        }

        return null;
    }

In effect, it finds the class containing the main method from the current call stack and returns its type.

run()

public ConfigurableApplicationContext run(String... args) {
//<1>
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
//<2>
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
// <3> 
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//<4>
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//<5>
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

<1> StopWatch

The current understanding is that a timer is primarily used to record the start-up time of spring boot.

<2> configureHeadlessProperty

Used to set up the system headless mode .Simply put, this mode tells the system that there is currently no monitor, keyboard or mouse.(Some java classes rely on devices such as monitors to run, which tells them they are not dependent.)

<3> SpringApplicationRunListeners

private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

getSpringFactoriesInstances is already an old face, get all META-INF/Spring.factoriesClasses of type SpringApplicationRunListener defined in the file.It is then stored in the Spring Application RunListeners.

The final starting method is to call the starting method of all SpringApplicationRunListener objects in SpringApplicationRunListeners.

Without customization, only one SpringApplicationRunListener is called - EventPublishingRunListener.

public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        Iterator var3 = application.getListeners().iterator();

        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
			//This is actually an example of all the ApplicationListener types described above
            this.initialMulticaster.addApplicationListener(listener);
        }

    }
   ......
    public void starting() {
	//Create an ApplicationStartingEvent event (to indicate that the application is started) and broadcast the event to see who is listening.
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }

The final listeners are as follows

LoggingApplicationListener: Configure LoggingSystem. BackgroundPreinitializer: Starts a thread in the background to do some time-consuming initialization tasks. DelegatingApplication: Forward to environment variable after listening for an eventContext.listener.classesSpecified event listeners. LiquibaseServiceLocatorApplicationListener: Liquibase is an open source tool for database migration.The listener provides special support for this migration tool.

Posted by evmace on Tue, 02 Jun 2020 01:51:44 -0700