• 1 introduction

This article is a series of long startup processes, including configuration initialization, environment initialization, and bean loading processes. This series will be explained in phases.

The article is based on spring Boot2.3. x series of source code, github source and the actual release may be slightly different, but the sorting process is not much different. I write an article for the first time, if there is a mistake or misleading welcome comment correction. The article as a whole may be wordy, but try to cover every important aspect of the process.

  • 2 SpringApplication initialization

public SpringApplication(ResourceLoader resourceLoader, Class<? >... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); This.primarysources = new LinkedHashSet<>(arrays.asList (primarySources)); / / web service types, now is not introduced a reactive web difference about this. WebApplicationType = webApplicationType. DeduceFromClasspath (); / / get ApplicationContextInitializer subclasses including custom class, SetInitializers ((Collection) operation used to call // customizing context loaders through applyInitializers in the prepareContext method before applicationContext container refresh getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); / / infer from the stack information that the current start class / / interested can also learn about this. MainApplicationClass = deduceMainApplicationClass (); }Copy the code

GetSpringFactoriesInstances, from the name of the method can probably know is to obtain ApplicationContextInitializer (application context initialization) and ApplicationListener instance factory, according to the type of the incoming class.

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<? >[] parameterTypes, Object... args) { ClassLoader classLoader = this.getClassLoader(); Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }Copy the code

The interior of the getSpringFactoriesInstances leads to the two methods, loadFactoryNames and createSpringFactoriesInstances. Let’s break it down one by one.

LoadFactoryNames:

public static List<String> loadFactoryNames(Class<? > factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); // The getOrDefault method used in Map is rarely used. The default value is set in advance, and the default value is returned if the result is not obtained in Map. return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@nullable ClassLoader ClassLoader) { String> result = (MultiValueMap)cache.get(classLoader); <URL> urls = classLoader! = null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); UrlResource resource = new UrlResource(url); // Load configuration through path, Will configure the attribute is transformed into the form of a Map / / Properties for a HashMap subclasses Properties Properties. = PropertiesLoaderUtils loadProperties (resource); LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class = LinkedMultiValueMap class List LinkedMultiValueMap result = new LinkedMultiValueMap(); Put (classLoader, result); return result; }Copy the code

If you’ve implemented or seen the Start package for SpringBoot, you should now have a Spring. factories file in the start jar that records the configuration classes. That’s where the files are scanned and loaded and the path is stored in the cache. In addition, also can see from here is the spring with a lot of Java at the bottom of things, such as this. Here getSystemResources, custom Map class, etc. Often read the source code may understand a lot of useless SAO operation.

This leaves the question why the source code is passed down as a classloader parameter instead of using this.getClassLoader directly in the current class.

All I’ve found is the classes that need to be loaded, but they haven’t been loaded yet.

CreateSpringFactoriesInstances:

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<? >[] parameterTypes, ClassLoader ClassLoader, Object[] args, Set<String> names) {// Get class objects, This classUtils is also of interest because it initializes a portion of Java's basic load speed at startup. > instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); // Get a Constructor of the specified parameter type. > constructor = instanceClass.getDeclaredConstructor(parameterTypes); // Get an instance of the // instantiateClass method via constructors and parameters. This is where you can learn more about reflection applications than just class.forname. If you have time, you can also learn about the methods and usage of these utility classes. And relative specifications T instance = BeanUtils instantiateClass (constructor, args); }Copy the code

So far, from spring. Factories all file to load the configuration, and the initialization and ApplicationContextInitializer (application context initialization) and ApplicationListener related instance of the factory. As for what they do, let’s move on.

It’s important to note that this is instantiating the Java equivalent of the new keyword and is not put into Spring’s bean container.

  • 3 The run method is executed

Before Posting the source code for the run method, it should be noted that the methods used before and after loading the context (that is, initializing the bean) may vary from version to version, but it generally doesn’t matter much.

Source:

The run method on the basis of the / / public ConfigurableApplicationContext run (String... StopWatch StopWatch = new StopWatch(); // Start the timer stopwatch.start (); / / initializes the application context ConfigurableApplicationContext context = null; / / initialize the abnormality report Collection Collection < SpringBootExceptionReporter > exceptionReporters = new ArrayList (); // Configure the system parameter java.awt.headless, which simply tells the service that it needs to run without peripherals and some hardware, related to graphics processing. this.configureHeadlessProperty(); / / publish event listeners collection, note that this is an asynchronous event SpringApplicationRunListeners listeners = this. GetRunListeners (args); / / the run () method starts, this method is called immediately, can be used in most early to do some work in the initialization / / SpringApplicationRunListener phase of each method in call. listeners.starting(); Collection exceptionReporters; Try {/ / initializes the application default arguments ApplicationArguments ApplicationArguments = new DefaultApplicationArguments (args); ConfigurableEnvironment = this.prepareEnvironment(Listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); // Create print class Banner printedBanner = this.printBanner(environment); / / create the application context context = this. CreateApplicationContext (); / / ready to report exceptionReporters = this. GetSpringFactoriesInstances (SpringBootExceptionReporter. Class, new Class[]{ConfigurableApplicationContext.class}, context); PrepareContext (context, environment, listeners, applicationArguments, printedBanner); // Refresh the application context this.refreshContext(context); This.afterrefresh (context, applicationArguments); Stopwatch.stop (); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } // Listeners. Started (context) are executed after the application context is started; // Execute all runners, CommandLineRunner&ApplicationRunner this.callRunners(context, applicationArguments); } notice (var10, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try {// Application context-ready events.running (context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); }}Copy the code

Run includes the whole startup process, this section only explains to the printing of banner, and simply say how to customize their own exclusive startup graphics, although there is no egg use, but happy!

Choose what to focus on in this section:

StopWatch StopWatch = new StopWatch(); // Start the timer stopwatch.start (); / / initializes the application context ConfigurableApplicationContext context = null; / / initialize the abnormality report Collection Collection < SpringBootExceptionReporter > exceptionReporters = new ArrayList (); // Configure the system parameter java.awt.headless, which simply tells the service that it needs to run without peripherals and some hardware, related to graphics processing. this.configureHeadlessProperty(); / / publish event listeners collection, note that this is an asynchronous event SpringApplicationRunListeners listeners = this. GetRunListeners (args); / / the run () method starts, this method is called immediately, can be used in most early to do some work in the initialization / / SpringApplicationRunListener phase of each method in call. listeners.starting(); Collection exceptionReporters; / / initialize the application default arguments ApplicationArguments ApplicationArguments = new DefaultApplicationArguments (args); ConfigurableEnvironment = this.prepareEnvironment(Listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); // Create print class Banner printedBanner = this.printBanner(environment);Copy the code

Start with the timer StopWatch and think of the contributors to the Spring framework who would use System.currentTimemillis () directly to calculate. This principle is not complicated, internal use is also System. NanoTime with higher accuracy of nanoseconds, and in many open source toolkits are provided, such as Apache Commons, Hutool, etc., we commonly used, but the internal method is not completely the same, Spring provides more like running race timer, You can stop multiple times.

ConfigureHeadlessProperty: setting up the Java. The awt. Headless properties, tell a Java application, the current running environment lack of keyboard, mouse, display devices, such as you have strong myself.

Listener:

 SpringApplicationRunListeners listeners = this.getRunListeners(args);
 listeners.starting();
Copy the code

Here you see what listeners are used for during initialization, remember that listeners are used for the entire startup process. Is not the custom in our daily used to handle asynchronous event listeners, they implement the interface is different, to realize here is that the SpringApplicationRunListener from name is easy to see, is to use the context to start, The one we use is ApplicationListener. It is also important to note that starting is not understood as starting listeners, but rather executes listeners related to pre-start preparations. This can be easily understood from the source code:

public interface SpringApplicationRunListener {
    void starting();

    void environmentPrepared(ConfigurableEnvironment environment);

    void contextPrepared(ConfigurableApplicationContext context);

    void contextLoaded(ConfigurableApplicationContext context);

    void started(ConfigurableApplicationContext context);

    void running(ConfigurableApplicationContext context);

    void failed(ConfigurableApplicationContext context, Throwable exception);
}

Copy the code

In addition to starting, environmentPrepared, contextPrepared, contextLoaded, Started, Runing, etc. Corresponds to the stages of the startup process.

On the internal principle of the listener here is not expanded to say, single out can also water a ha ha! Dig a hole, dig a hole!

So at the time of custom start starts the jar, if you want to use SpringApplicationRunListener, you need to clear your mind application stage.

So far, so simple. Now I’m going to start a little bit more complicated. Default parameter initialization, although we almost never pass arguments when using the run method, but they support! That must understand!

 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
Copy the code

The ApplicationArguments object itself has nothing to say, providing parameter initialization for the application context. But it has an inner class

private static class Source extends SimpleCommandLinePropertySource { ... . }Copy the code

Looking at the name of the parent class, you should be able to guess what this andPropertySourceSo let’s confirm the class diagram.

As you can see from the diagram, arguments passed to the run method can be accepted as command arguments to comAndLine, and arguments can be wrapped as PropertySource objects and put into ApplicationArguments for use. The parameters are eventually passed to the top-level PropertySource via the constructor super keyword.

About SimpleCommandLinePropertySource this is not said, here is the command line parameter Settings, such as: – name = zhangsan, the other set here can get into through @ Value, this belongs to the scope of SAO operation, so rarely used. This is where properties in configuration files can be replaced, such as the server.port property in application.properties can be replaced with the –server.port parameter, which is essentially executed as a startup parameter, Java-jar –server.port=8080 Has a higher priority than the configuration file.

You also need to be careful about the format of the input parameter, and the — and non— are parsed differently here.

    public PropertySource(String name, T source) {
        this.logger = LogFactory.getLog(this.getClass());
        Assert.hasText(name, "Property source name must contain at least one character");
        Assert.notNull(source, "Property source must not be null");
        this.name = name;
        this.source = source;
    }
Copy the code

PropertySource is also an important class in Spring. It is a top-level configuration resource wrapper class that is used in many places. Like BeanDefinition, PropertySource is one of spring’s canonical applications and is easy to extend.

PrepareEnvironment: Prepare the startup environment

    ConfigurableEnvironment environment = this.prepareEnvironment(listeners,applicationArguments);
    this.configureIgnoreBeanInfo(environment);
    Banner printedBanner = this.printBanner(environment);
Copy the code

There are two parts: initializing the environment configuration and printing the banner

Initialization environment configuration source:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments ApplicationArguments) {// Get the environment ConfigurableEnvironment environment = this.getOrCreateEnvironment(); / / configure the environment parameters and profiles. The active information enclosing configureEnvironment ((ConfigurableEnvironment) environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach((Environment)environment); listeners.environmentPrepared((ConfigurableEnvironment)environment); this.bindToSpringApplication((ConfigurableEnvironment)environment); if (! this.isCustomEnvironment) { environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass()); } ConfigurationPropertySources.attach((Environment)environment); return (ConfigurableEnvironment)environment; }Copy the code

As usual, one by one. GetOrCreateEnvironment: Gets the environment class

private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment ! = null) { return this.environment; } else { switch(this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); }}}Copy the code

WebApplicationType: Parameters are initialized in the SpringApplication constructor (some of the source code may be initialized directly when created).

Here the results returned are generally StandardServletEnvironment now we did not use responsive web framework, namely spring 5 launching webflux. We need to learn that in the future.

StandardServletEnvironment in the constructor of the superclass AbstractEnvironment also configured resources MutablePropertySources (environmental resources preservation) and propertyResolver initial parsing (resources) To obtain the system configuration and environment variables and other configuration information, and then used to say more details.

ConfigureEnvironment: indicates the configuration environment

    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService)conversionService);
        }

        this.configurePropertySources(environment, args);
        this.configureProfiles(environment, args);
    }
Copy the code

ConversionService: Sets the transformation service for the environment. The configuration parameter addConversionService defaults to true, so it must be initialized.

ApplicationConversionService. GetSharedInstance () is not complicated, can know from the name of the service for context switches, Here getSharedInstance LanHanShi ways to obtain a ApplicationConversionService singleton.

Some conversion and formatting services are initialized within the constructor. For example, String to number, Date to String, etc. It is also used when the configuration file is read and the property is transferred to the configuration class or the @Value annotation binding parameter.

An extension to the implementation of the singleton pattern, hungry and lazy, normally lazy is not thread-safe. Hangry is inherently thread-safe. So if you use lazy style, you need to address thread-safety issues. Here getSharedInstance demonstrates one of the standard thread-safe lazy patterns, which can also be implemented by enumerating classes.

GetSharedInstance = getSharedInstance

    public static ConversionService getSharedInstance() {
        ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;
        if (sharedInstance == null) {
            Class var1 = ApplicationConversionService.class;
            synchronized(ApplicationConversionService.class) {
                sharedInstance = ApplicationConversionService.sharedInstance;
                if (sharedInstance == null) {
                    sharedInstance = new ApplicationConversionService();
                    ApplicationConversionService.sharedInstance = sharedInstance;
                }
            }
        }

        return sharedInstance;
    }
Copy the code

This goes too far, but I really feel that the source code, the writing of the code and the use of some Java native tools are very good, many small points are worth learning. It’s not just a matter of figuring out how to start a Spring startup, but how the big guys code it.

Go ahead and get the transformation service and set it in the environment object for use.

ConfigurePropertySources configurePropertySources Configures resource processing.

protected void configurePropertySources(ConfigurableEnvironment environment, String [] args) {/ / this property is initialized in the constructor of AbstractEnvironment MutablePropertySources sources = environment. GetPropertySources ();  // defaultProperties defaults to empty if (this.defaultProperties! = null && ! this.defaultProperties.isEmpty()) { sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties)); } / / when it comes to in front of the commandline configuration parameter, if (this. AddCommandLineProperties && args. The length > 0) {String name = "commandLineArgs"; 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)); }}}Copy the code

This saves the incoming configuration resources and the default configuration resources into the environment’s resources.

By the way, it turns out that Spring also has a lot of dead constants, although for the most part this property is relatively unchanged. But I still want to make fun of it!

ConfigurationPropertySources. Attach: Settings: configurationProperties configuration properties

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))); }}Copy the code

Verify that configurationProperties is included in the native configuration property list. If not, add configurationProperties. If yes, check whether the object is the same. If not, delete it and add it again. Here is more interesting, sources is to obtain the environmental objects, and then will get results through SpringConfigurationPropertySources encapsulation, Put it into Sources as a new configurationProperties property for Sources.

So far the purpose of this has not been clear. Keep looking down with doubt.

Listeners. EnvironmentPrepared: this is not much said, the environment is ready to perform the related to this listener.

BindToSpringApplication:

This one is a little bit more interesting, from the name, binding to SpringApplication, binding to what?

protected void bindToSpringApplication(ConfigurableEnvironment environment) { try { Binder.get(environment).bind("spring.main", Bindable.ofInstance(this)); } catch (Exception var3) { throw new IllegalStateException("Cannot bind to SpringApplication", var3); }}Copy the code

Binder.get to see how binder works, binder.

    public static Binder get(Environment environment, BindHandler defaultBindHandler) {
        Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);
        PropertySourcesPlaceholdersResolver placeholdersResolver = new PropertySourcesPlaceholdersResolver(environment);
        return new Binder(sources, placeholdersResolver, (ConversionService)null, (Consumer)null, defaultBindHandler);
    }
Copy the code

From here you can see a ConfigurationPropertySources. The get method, from the environment obtained ConfigurationPropertySource iterator configuration of resources.

Its source code:

    public static Iterable<ConfigurationPropertySource> get(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        MutablePropertySources sources = ((ConfigurableEnvironment)environment).getPropertySources();
        ConfigurationPropertySourcesPropertySource attached = (ConfigurationPropertySourcesPropertySource)sources.get("configurationProperties");
        return attached == null ? from((Iterable)sources) : (Iterable)attached.getSource();
    }

Copy the code

You can see why you set the configuration resource to configurationProperties, where you get the specified configuration resource in the environment through the ConfigurableEnvironment class. The final result returned here is of type Iterable because the list is stored in configurationProperties.

Let’s go back to the source code for binder.get. In addition to fetching configuration resources in the environment class, a configuration resource placeholder parser is initialized

  PropertySourcesPlaceholdersResolver placeholdersResolver = new PropertySourcesPlaceholdersResolver(environment);
Copy the code

PropertySourcesPlaceholdersResolver:

public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<? >> sources, PropertyPlaceholderHelper helper) { this.sources = sources; this.helper = helper ! = null ? helper : new PropertyPlaceholderHelper("${", "}", ":", true); }Copy the code

To make this intuitive, replace the ${xx} placeholder in the configuration file.

We won’t expand the bind method here, but bind the configuration under Spring. Main to the SpringApplication object. Attributes such as sources, bannerMode and so on are assigned to the current object.

As an added bonus, guessing the role of a class from its name field is really useful in Spring source code, such as the ConfigurableEnvironment in the above source code. He is one of the implementation class StandardServletEnvironment we mentioned above, the spring will environment configurable properties, mentioned in the interface. Both easy to extend and intuitive to show which properties can be modified through configuration.

Determine whether the environment object needs to be transformed

if (! this.isCustomEnvironment) { environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass()); }Copy the code

The isCustomEnvironment parameter defaults to false, which is mandatory here. Internally compares whether the current environment object is a StandardEnvironment subclass. If not, regenerate the StandardEnvironment environment object and convert the internal parameters into the new object

Methods the end, to perform the ConfigurationPropertySources. Attach method. The resource or environment classes may have already been modified. This reinitializes the configurationProperties in the environment configuration resource class.

That’s it, we’re done with the prepareEnvironment (environment object initialization process).

ConfigureIgnoreBeanInfo: Sets the BeanInfo to be ignored.

PrintBanner starts the printing of the banner.

Source:

    private Banner printBanner(ConfigurableEnvironment environment) {
        if (this.bannerMode == Mode.OFF) {
            return null;
        } else {
            ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader((ClassLoader)null);
            SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);
            return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);
        }
    }
Copy the code

This. BannerMode is initialized within the SpringApplication constructor (some versions of the source code are initialized at creation time). The default value is CONSOLE. The above source code is relatively simple, providing two printing methods and initializing a banner object. There are two different banner types available within bannerprinter. print.

    private Banner getBanner(Environment environment) {
        SpringApplicationBannerPrinter.Banners banners = new SpringApplicationBannerPrinter.Banners();
        banners.addIfNotNull(this.getImageBanner(environment));
        banners.addIfNotNull(this.getTextBanner(environment));
        if (banners.hasAtLeastOneBanner()) {
            return banners;
        } else {
            return this.fallbackBanner != null ? this.fallbackBanner : DEFAULT_BANNER;
        }
    }
Copy the code

GetImageBanner and getTextBanner, image and text modes. We haven’t printed it yet, we just initialized a banner object.

Private Banner getTextBanner(Environment Environment) {// Spring.banner. Location property, TXT String location = environment. GetProperty (BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION); Resource resource = this.resourceLoader.getResource(location); try { if (resource.exists() && ! resource.getURL().toExternalForm().contains("liquibase-core")) { return new ResourceBanner(resource); } } catch (IOException ex) { // Ignore } return null; } private Banner getImageBanner(Environment Environment) {//spring.banner. Location Specifies the file String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY); if (StringUtils.hasLength(location)) { Resource resource = this.resourceLoader.getResource(location); return resource.exists() ? new ImageBanner(resource) : null; } for (String ext : IMAGE_EXTENSION) { Resource resource = this.resourceLoader.getResource("banner." + ext); if (resource.exists()) { return new ImageBanner(resource); } } return null; }Copy the code

TXT is created under resource to replace the original banner information. This way is easier.

  • 4 summary

This article explains the actions from startup to before creating the context, including initialization of the SpringApplication (loading configuration classes in the Spring.Factories file, listeners, etc.), initialization of the runtime environment (initialization of the environment class and internal configuration resources), construction of the banner object, etc.

The Environment class will be used more frequently in the future. Configuration resources are obtained from this class, which provides the SpringBoot runtime Environment.

The article is wordy, many points are not detailed in-depth, if detailed, an article can not finish, such as listener and some tool classes.

Simple process: 1. Initialize the SpringApplication constructor 2. Load configuration resources (system configuration, environment variables, the project’s application.yml configuration, etc.) 4. Initialize environment parameters and inject configuration resources. 5. Create the banner object