Overview of SpringBoot/HelloWorld/Auto-assembly/Startup Class

SpringBoot

Learn video links to show respect: https://www.bilibili.com/video/BV1PE411i7CV

1. Overview

The Spring framework was created to address the complexity of enterprise application development and simplify development.

To reduce the complexity of Java development, Spring employs four key strategies:

  • Lightweight and minimally invasive programming based on POJO, everything is a bean;
  • Loose coupling is achieved through IOC, dependent injection (DI) and interface-oriented.
  • Declarative programming based on facets (AOP) and conventions;
  • Reduce style code by cutting faces and templates, RedisTemplate, xxxTemplate;

Spring Boot is the core idea of convention over configuration. By default, it helps developers set up many settings. Most Spring Boot applications require very little Spring configuration. It also integrates a number of commonly used third-party library configurations (such as Redis, MongoDB, Jpa, RabbitMQ, Quartz, and so on), which are available out of the box with almost zero configuration in Spring Boot applications.

The main advantages of Spring Boot are:

  • Getting started faster for all Spring developers
  • Out of the box, provides various default configurations to simplify project configuration
  • Embedded Containers Simplify Web Projects
  • No redundant code generation and XML configuration requirements
  • Many frameworks are integrated

2. Hello World

**Project Creation Method 1: ** Create a project using Spring Initializr's Web page

  1. open https://start.spring.io/
  2. Fill in project information
  3. Click the Generate Project button to build the project; download the project
  4. Unzip the project package and import it as a Maven project using IDEA, and proceed the next step until the project has been imported
  5. If it's the first time you use it, it may be slower, have more bags, need patience and wait for everything to be ready

**Project Creation Mode 2: ** Create a project directly using IDEA

  1. Create a new project
  2. By choosing spring initalizr, you can see that the default is to go to the Quick Build Tool for Official Networks.
  3. Fill in project information
  4. Select the component to initialize (check the Web for beginners)
  5. Fill in project path
  6. Waiting for the project to build successfully

Project structure analysis:

When the foundation project is created by the above steps, the following files are automatically generated:

  1. The main startup class of the program

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SpringbootLearnApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootLearnApplication.class, args);
        }
    
    }
    
  2. An application. Properrties configuration file for configuring database, Redis, etc. (SpringBoot officially recommends yaml)

  3. A test class

    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class SpringbootLearnApplicationTests {
    
        @Test
        void contextLoads() {
        }
    
    }
    
  4. A pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <!--    Parent Dependency-->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.xingyu</groupId>
        <artifactId>springboot-learn</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>springboot-learn</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>14</java.version>
        </properties>
    
        <dependencies>
        <!-- web Dependency: tomcat,dispatcherServlet,xml Wait-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <!--   SpringBoot unit testing     -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <!-- Remove Dependencies -->
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <!-- Packaging Plugins -->
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    
    

3. Auto-assembly

3.1 pom.xml

According to pom.xml file, the SpringBoot project relies on a parent project, which manages the project's resource filtering and plug-ins.

<!--    Parent Dependency-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

The dependency also depends on another parent dependency, Click to enter spring-boot-starter-parent.pom:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.3.4.RELEASE</version>
</parent>

This is where you really manage all the dependent versions in your SpringBoot application. SpringBoot's Version Control Center, Click to enter spring-boot-dependencies.pom:

<properties>
  <activemq.version>5.15.13</activemq.version>
  <antlr2.version>2.7.7</antlr2.version>
  <appengine-sdk.version>1.9.82</appengine-sdk.version>
  <artemis.version>2.12.0</artemis.version>
  <aspectj.version>1.9.6</aspectj.version>
  <assertj.version>3.16.1</assertj.version>
  <atomikos.version>4.0.6</atomikos.version>
    ......
    ......

It specifies a large number of dependent version information. This makes it unnecessary to write version information for future import dependencies by default, but if the imported package is not managed in the dependency, you will need to configure the version manually.

3.2 Main Startup Class

Default primary startup class, where:

  • The @SpringBootApplication annotation is used to label a main program class, indicating that this is a SpringBoot application
  • SpringApplication.run is used to start the SpringBoot application service
package com.xingyu.springbootlearn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootLearnApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootLearnApplication.class, args);
    }
}
3.3 @SpringBootApplication

Role of the @SpringBootApplication annotation: Labels indicate in a class that this class is the main configuration class for SpringBoot, and SpringBoot should run the main method of this class to start the SpringBoot application.

Click into the note and you can see that there are many other notes:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { 
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ......
}
3.3.2 @SpringBootConfiguration

Role: The configuration class of SpringBoot, labeled on a class, indicates that this is a configuration class of SpringBoot.

Click to enter:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
......
}

Found the @Configuration annotation, where @Configuration means this is a configuration class, which is the xml configuration file for Spring. Click to enter:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    ......
}

The @Component inside indicates that the startup class itself is only a component of Spring and is responsible for starting the application.

3.3.1 @EnableAutoConfiguration

The @EnableAutoConfiguration annotation is important in Spring and corresponds to elements in the XML configuration.

Effect:

  • Automatically scan and load qualified components or beans to load this bean definition into the IOC container
  • To turn on auto-configuration, we used to need something to configure ourselves, but now SpringBoot can configure it for us automatically
  • Tell SpringBoot to turn on auto-configuration so that it will take effect.

Click to enter:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ......
}
  • @AutoConfiguration Package: Automatic Configuration Package

    @Import({Registrar.class})
    public @interface AutoConfigurationPackage {
    }
    //Registrar.class effect:
    //Scan the package of the main boot class and all components in all subpackages under the package into the Spring container;
    

    @import:Spring underlying comment@import, importing a component into a container

  • @Import({AutoConfigurationImportSelector.class}): Import components to containers

    • AutoConfiguration Import Selector: Auto-configure import selector

Which selectors will AutoConfigurationImportSelector import and click to enter them:

1. There is one such method in this class

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         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;
}

2. This method calls the static method of the SpringFactoriesLoader class again! We enter the SpringFactoriesLoader class loadFactoryNames() method

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

3. Let's continue clicking to view the loadSpringFactories method

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //Get the classLoader, and you can see that what you get here is the class itself labeled by EnableAutoConfiguration
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            //To 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();
			//Traverse 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 factoryTypeName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryImplementationName = var9[var11];
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

4. Found a file that appears many times: spring.factories. Open spring from source. Factories, see a lot of auto-configuration files; This is where automatic configuration comes in

......
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
......

You can see that all of these are JavaConfig configuration classes, with some beans injected.

The real implementation of automatic configuration is to search for all META-INF/spring from the classpath. Factories configuration file and its corresponding org.springframework.boot.autoconfigure. Configuration items under the package are instantiated by reflection into IOC container configuration classes in the form of JavaConfig labeled @Configuration, which are then aggregated into an instance and loaded into the IOC container.

Conclusion:

  1. SpringBoot starts from META-INF/spring in the class path. Get the value specified by EnableAutoConfiguration in factories
  2. Import these values into the container as auto-configuration classes, and the auto-configuration classes will take effect to help us do the auto-configuration work.
  3. The entire J2EE solution and automatic configuration are in the jar package of springboot-autoconfigure;
  4. It will import a very large number of auto-configuration classes (xxxAutoConfiguration) into the container, that is, all the components needed to import this scene into the container and configure them.
  5. With automatic configuration classes, we avoid writing configuration injection function components manually, etc.

4. How to Start

Startup class for SpringBoot application:

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

SpringApplication.run analysis:

There are two main parts to analyze this method, one is the instantiation of SpringApplication, the other is the execution of run method.

This class mainly does the following four things:

1. Inform if the type of application is a normal project or a Web project

2. Find and load all available initializers and set them in the initializers property

3. Find all application listeners and set them in the listeners property

4. Inform and set the definition class of the main method to find the main running class

View the constructor:

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

run method flow:

Tags: Java Maven Spring Spring Boot

Posted by Skara on Fri, 13 May 2022 20:28:54 +0300