SpringBoot growth 2: analyzing SpringBoot from HelloWorld

Posted by parijat_php on Tue, 28 Sep 2021 00:52:41 +0200

In the previous section, we mentioned that when you know a new technology, you usually start with an introductory HelloWorld, and then read some introductory documents, books and videos to master its basic use.

In this section, I will take you to start with HelloWorld, first find out the core context of SpringBoot, and then gradually analyze SpringBoot thoroughly, so as to master it.

Analyze SpringBoot from the HelloWorld portal

First, we build a 2.2.2 version of SpringBoot from the official documents, add two starters, mybatis plus boot starter and spring boot starter web, use Maven for project and dependency management, and configure a local mysql. I believe this is relatively simple for you, so I won't repeat it one by one.

After the above basic construction, you will have a SpringBoot HelloWorld level entry similar to the following.

package org.mfm.learn.springboot;


import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("org.mfm.learn.springboot.mapper")
@SpringBootApplication
public class LearnSpringBootApplication {

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

}

In the previous section, you know that SpringBoot defines a SpringApplication web application startup process. The portal executes the main function to start a JVM process through a Java jar command, runs the internal tomcat, listens to a default 8080 port, and provides Web services.

The first and most important thing in the whole process is the SpringApplication defined by SpringBoot. Let's take a look at how it creates new.

Spring application core component diagram at creation time

Code analysis when creating spring application

In the above example code, the main method executes the run method of SpringApplication, 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);
}

The core input parameter of the run method is the class of the main function + the parameter of the main function args, and then a SpringApplication object is directly created. Let's take a look at how the concept defined by SpringBoot is created, and what are the core components when it is created?

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();
}

The core context of spring application creation

The core context of spring application creation is relatively simple:

1) ResourceLoader indicates the resource loader. It is not clear what it is. It is null by default.

2) webApplicationType infers the current web application type through a deduceFromClasspath method.

3) After that, setInitializers and setListeners are set, which are a pile of initializers and listeners respectively, which are obtained through the getSpringFactoriesInstances method.

4) The main resource class for startup is recorded through primarySources and mainApplicationClass, that is, learnsprinbootapplication.class in HelloWorld.

The above is the result from my brain after I first looked at a context of this class.

The first time you look here, you must not know anything. You don't know what each variable is for and what it is. It doesn't matter. For the first time, you just need to be familiar with its context. It is enough to know that two collection variables Initializer and Listener are set here. You can set esourceLoader and mark some types and classes.

After you have time, you can understand the role of each variable or component one by one. This is not the idea of context before details, is it?

Detailed analysis of spring application creation

You can slowly disassemble each step above to see what each component does. This is the study of details, which can be done step by step.

You can study what ResourceLoader is? You can see from its class annotation that this ResourceLoader class is responsible for loading classes and various configuration files under ClassPath using ClassLoader. (if you don't know the ClassLoader mechanism of the JVM and what to load, you can go to baidu and google to find out). Here you can think further. It is designed as an interface that can implement different class loaders to load resources.

How is webApplicationType inferred? It is to infer the type according to the fully qualified name of the class defined by several static variables and whether there is a corresponding class under the classPath. Web starter is used. Applications of Servlet type are inferred by default.

As for primarySources and mainApplicationClass, the two variables record learnsprinbootapplication.class, which is generally considered for automatic configuration after scanning, indicating the package name and class under which it is started.

How to set the last two collection variables Initializer and Listener is worth studying.

The basic principle is to scan all the files in the META-INF/spring.factories directory under the classPath through the ClassLoader, obtain all the corresponding implementation classes through the specified factoryType, that is, the interface name, instantiate them into objects and return them to a list.

For example, factoryType=ApplicationContextInitializer returns all the implementation classes defined by this interface in META-INF/spring.factories and instantiates it as a List ApplicationContextInitializer.

Similarly, ApplicationListener obtains a collection of list applicationlisteners.

In fact, there are many details. Class loader, cache mechanism and reflection mechanism are used. Interested students can study them carefully.

Here we summarize the idea of focusing on the big and letting go of the small into one sentence: obtain the instance object list of all implementation classes of an interface at the specified location of the classPath through the tool method and the classLoader.

Here is the instance object list of ApplicationContextInitializer and ApplicationListener interfaces.

You can learn knowledge in details and in context. You must have this idea slowly. The meaning of grasping the big and letting go of the small is to let you know the key points and key points, rather than let you discard the details. The two do not conflict. We must pay attention to this.

Finally, after detailed analysis, draw a simple component diagram to summarize:

Context analysis of spring application run method

After getting familiar with the creation of spring application, it's time to analyze its run method.

In fact, in the previous section, we introduced the startup process of spring application. This is a high-level summary of the core context of the run method. In fact, the core of the run method is the blue part in the figure below:

The context of run method can be summarized as follows:

1) Automatic assembly configuration

2) Creation of Spring container

3) web container startup (Tomcat startup)

However, the execution of run method is not so simple. It also mixed many complicated logic, and there are interesting extensions and make complaints about Tucao. This is the advantage and disadvantage of every framework. Let's first touch the context of the run method and introduce some terms to you, otherwise you may not understand the code details later.

The context of spring application run method is further analyzed

To further analyze the context of the run method, you first need to be familiar with several terms, which is a bit like the general language of DDD. Understanding these languages and understanding SpringBoot and Spring will be more handy.

Terms: Context/BeanFactory/Environment

ConfigurableApplicationContext, the container is usually called ApplicationContext or BeanFactory, and the context is also called container for short. ApplicationContext wraps BeanFactory and encapsulates more advanced API s.

ConfigurableEnvironment is an abstraction of the configuration file. The key value of any configuration file such as properties or yml will be encapsulated into an implementation class of this class.

After getting familiar with these terms, let's take a look at the run method code of spring application.

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);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

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

The above code mainly executes a bunch of methods. As can be seen from the method names, they all revolve around the terms Context and Environment. That is, the logic organized around containers and configuration files.

A lot of logic is interspersed after the creation, refresh and refresh of the whole Spring container.

In addition, there are several key extension points in the whole run method of SpringBoot. Design extension entries such as SpringApplicationRunListeners and Runners. The creation and refresh of containers also need their own extension points. The enhanced extension of containers, such as beanfactoryprocessor, and the added extension of beans, such as beanPostProcessor. However, these are later words.

I will use a diagram to summarize the context of the above run method:

(* black is the intuitive extension logic. White is the literal understanding of each method of the run method, but there are many extension points and more things to do in each step, which makes you feel a little confused. The blue part summarizes the core logic. That is, SpringBoot startup. To put it bluntly, our core is to find these three points: automatic assembly configuration and creation of Spring container Build and start the web container.)

At this time, you must learn to focus on the big and let go of the small. Then take these three key steps to understand SpringBoot. Other implementations can study and analyze its design ideas separately, such as how to consider the design of each extension point. What can we refer to. This is the most important thing to learn from SpringBoot.

To sum up, when a technology looks more complex, you should understand the principle along the core context and learn the highlight design ideas of various details. Don't get caught up in a detail. It's important to think more. You must remember this. In the follow-up growth notes, I will gradually take you to experience this.

Summary

All right, a simple summary.

The main ideas are as follows:

1) The thought of context before details, the thought of focusing on the big and letting go of the small, excluding the unimportant and analyzing the most important.

2) You can learn knowledge in details and in context. You must have this idea slowly. The meaning of grasping the big and letting go of the small is to let you know the key points and key points, rather than let you discard the details. The two do not conflict. We must pay attention to this.

3) It's important to think more. Understand the principles along the core context, learn the bright spot design ideas of various details, and never fall into the knowledge itself.

Main knowledge learned:

Today, we mainly look at the creation of spring application, its core components, the run method executed after creation, what it has done and what the context is.

After getting familiar with these contexts, the rest is simple. Gradually analyze each detail to see if there are some points worthy of our study and which points are not suitable.

See you next time!

This article is composed of blog one article multi posting platform OpenWrite release!

Topics: Java Spring Boot