Preface:

In our actual project development process using Spring Boot, we often only need a few very simple annotation configuration to get the application up and running, compared to the traditional Spring project, this improvement greatly improves our development efficiency.

We tend to focus on too much use level, in order to quickly complete the business development, are often ignored the attention of the principle of the framework underlying run, then comb next Spring in all the Boot of the underlying principle of operation, and through the combination of images and show you, hope to your job or job interview can help!

Before we begin, let’s take a look at the Pom.xml file for the SpringBoot project.

Body:

Father relies on

It mainly relies on a parent project, mainly manages the project’s resource filtering and plug-ins!

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.26..RELEASE</version> <relativePath/> <! -- lookup parent from repository --> </parent>Copy the code

If you click in, there is another parent dependency:

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.26..RELEASE</version> <relativePath>.. /.. /spring-boot-dependencies</relativePath> </parent>Copy the code

This parent dependency is the place that really manages all dependent versions in the SpringBoot application, the Version control center of SpringBoot. Click in to see:

 <properties>
    <activemq.version>5.1512.</activemq.version>
    <antlr2.version>2.77.</antlr2.version>
    <appengine-sdk.version>1.979.</appengine-sdk.version>
    <artemis.version>2.101.</artemis.version>
    <aspectj.version>1.9. 5</aspectj.version>
    <assertj.version>3.132.</assertj.version>
    <atomikos.version>4.06.</atomikos.version>
	...
    <versions-maven-plugin.version>2.7</versions-maven-plugin.version>
    <webjars-hal-browser.version>3325375</webjars-hal-browser.version>
    <webjars-locator-core.version>0.41</webjars-locator-core.version>
    <wsdl4j.version>1.63.</wsdl4j.version>
    <xml-maven-plugin.version>1.02.</xml-maven-plugin.version>
    <xmlunit2.version>2.64.</xmlunit2.version>
  </properties>
Copy the code

We can see that there are a lot of integrated dependencies in there, so we don’t need to write versions to import dependencies by default; However, if the imported package is not managed in dependencies, you need to manually configure the version.

Main startup class:

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); }}Copy the code

Here’s an analysis of the various notes included:

@SpringBootApplication

SpringBoot should run this class’s main method to start the SpringBoot application.

Check it out, and it includes a lot of notes:

@Target({ElementType.TYPE}) // Scope of annotations, where TYPE is used to describe classes, interfaces (including package annotation types), or enum declarations
@Retention(RetentionPolicy.RUNTIME) // The annotation life cycle is kept in the class file (three life cycles)
@Documented // indicates that the annotation should be logged by Javadoc
@Inherited  // Subclasses can inherit this annotation
@SpringBootConfiguration	
@EnableAutoConfiguration
@ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} )
public @interface SpringBootApplication {
    ...
}
Copy the code

@ComponentScan

This annotation is important in Spring and corresponds to elements in the XML configuration.

What it does: Automatically scans and loads qualified components or beans, and loads the bean definition into the IOC container

@SpringBootConfiguration

SpringBoot configuration class, marked on a class, indicating that this is a SpringBoot configuration class.

Click on this note to see:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor( annotation = Configuration.class )
    boolean proxyBeanMethods(a) default true;
}
Copy the code
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor( annotation = Component.class )
    String value(a) default "";

    boolean proxyBeanMethods(a) default true;
}
Copy the code

@Configuration indicates that this is a Configuration class, and the Configuration class is the corresponding Spring XML Configuration file;

The @Component indicates that the launcher class itself is a Component in Spring that launches the application.

Go back to the SpringBootApplication annotations.

@EnableAutoConfiguration

@EnableAutoConfiguration

Function: Enable automatic configuration function, we used to need to configure their own things, but now SpringBoot can automatically configure for us; @enableAutoConfiguration tells SpringBoot to enable automatic configuration so that the automatic configuration takes effect.

@AutoConfigurationPackage

Function: Automatic configuration package

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
Copy the code

@Import

Spring uses the @import annotation to import a component into the container.

Registrar. Class Will scan the main launcher package and all the components in the sub-package to the Spring container.

@Import({AutoConfigurationImportSelector.class})

Function: Imports components into a container

AutoConfigurationImportSelector: automatic configuration import selector, then it will import the selector which components? Let’s click on this class to see the source code:

  1. This class has a method like this:
// Get the candidate configuration
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    / / here getSpringFactoriesLoaderFactoryClass () method
    // Return the annotation class that started the automatic import configuration file; EnableAutoConfiguration
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}
Copy the code
  1. This method again calls the static method of the SpringFactoriesLoader class! We go to the loadFactoryNames() method of the SpringFactoriesLoader class
public static List<String> loadFactoryNames(Class<? > factoryClass,@Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    // Here it calls the loadSpringFactories method again
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
Copy the code
  1. LoadSpringFactories method
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    // Get the classLoader, and you can see that this is the EnableAutoConfiguration class itself
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if(result ! =null) {
        return result;
    } else {
        try {
            // Get a resource "meta-inf /spring.factories"Enumeration<URL> urls = classLoader ! =null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();
 
            // Iterate over the read resource and encapsulate it as a Properties
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();
                
                while(var6.hasNext()) { Entry<? ,? > entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());int var10 = var9.length;
                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); }}}Copy the code
  1. spring.factories

The above excerpt from the Meta-INF/Spring.Factories configuration file in SpringBoot’s autoconfigure dependency package is a good illustration.

** @enableAutoConfiguration Automatic configuration principle: ** Search all meta-INF /spring.factories configuration files from the classpath, And will the org. Springframework. Boot. Autoconfigure. EnableutoConfiguration corresponding configuration items through reflection (Java Refletion) instantiated into the corresponding IoC container Configuration class in JavaConfig form annotated with @Configuration, then aggregated into one and loaded into the IoC container.

For most frameworks that need to be integrated with Spring Boot by third parties, or common components that need to be abstracts in our daily development, various Starter components can be easily customized out of the box thanks to this mechanism. Users of these components, however, often just need to import their dependencies without any additional configuration!

That’s how SpringBoot works.

Take a look at the SpringBoot startup schematic online:

SpringApplication executes the process

SpringApplication. Analysis of the run

Analysis of this method is mainly divided into two parts, one is the instantiation of SpringApplication, the second is the execution of run method;

Instantiation of the SpringApplication

Before calling Run, a SpringApplication object instance is instantiated;

The basic call flow of the SpringApplication.run() method:

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((ResourceLoader)null, primarySources);
}
Copy the code

SpringApplication method:

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.lazyInitialization = 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();
}
Copy the code

After the SpringApplication instance is initialized and set up, the run method is executed:

Call the run method

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        // Configure the properties
        this.configureHeadlessProperty();
        // Get the listener
    	/ / use loadFactoryNames method from path MEAT - INF/spring. Find all SpringApplicationRunListener factories
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
         // Start the listener
	    / / call each SpringApplicationRunListener starting method
        listeners.starting();

        Collection exceptionReporters;
        try {
            // Encapsulate the arguments in the ApplicationArguments object
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // Prepare the environment
   	        / / trigger monitor event - call each SpringApplicationRunListener environmentPrepared method
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // Take the Banner out of the environment and print it
            Banner printedBanner = this.printBanner(environment);
            // Depending on whether a Web container is created for a Web environment or a normal IOC container
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // Prepare the context
       		// 1. Save the environment to the container
      		/ / 2. The trigger monitor events - call each SpringApplicationRunListeners contextPrepared method
    		/ / 3. Call ConfigurableListableBeanFactory registerSingleton method applicationArguments and printedBanner injections into the container
    	    / / 4. The trigger monitor events - call each SpringApplicationRunListeners contextLoaded method
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // Refresh the container, complete the component scan, create, load, etc
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
			/ / trigger monitor event - call each SpringApplicationRunListener started method
            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 the container
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw newIllegalStateException(var9); }}Copy the code

Conclusion:

Springapplication.run does two things

  1. Create the SpringApplication object; The event listener is saved during object initialization, the container initializes the class and determines whether it is a Web application, and the main configuration class containing the main method is saved.
  2. Call the run method; Prepare the Spring context, complete container initialization, creation, loading, etc. Triggers different events for listeners at different times.