SpringBoot Travel Part 6 - Starting Principles and Custom starter

Keywords: Java Spring SpringBoot Maven snapshot

I. Introduction

One advantage of SpringBoot is Starter, because SpringBoot has many out-of-the-box Starter dependencies that make our development easy, and we don't need to focus too much on the configuration of the framework.

In daily development, we will also customize some Starters, especially now the micro service framework, where a single project is divided into several individual projects that reference some of the company's components. At this time, we define Starters so that these individual projects can be built up quickly. We just need to focus on business development.

Before that, we'll take a closer look at how SpringBoot works.Then you'll customize the starter.

2. Startup principle

To understand how it works, we can follow the code step by step in Debug mode, starting with the entry method:

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
      String[] args) {
   return new SpringApplication(primarySources).run(args);
}

Here you create a SpringApplication object and call the run method

2.1 Create a SpringApplication object

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //Save the main configuration class 
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //Determine web Application Type
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //Find it under the class path META-INF/spring.factories All configured ApplicationContextInitializer;And save it
    setInitializers((Collection) getSpringFactoriesInstances(
          ApplicationContextInitializer.class));
    //Find it under the class path ETA-INF/spring.factories All configured ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //Found in multiple configuration classes main Main Configuration Class for Methods
    this.mainApplicationClass = deduceMainApplicationClass();
}

As you can see from this method, this

Step 1: Save the main configuration class.

Step 2: Determine the type of web application.

Step 3: the setInitializers method, which takes the parameter we see as getSpringFactoriesInstances(ApplicationContextInitializer.class), and we'll look further at getSpringFactoriesInstances

Enter this method again:

This is where we find all the ApplicationContextInitializer s for the META-INF/spring.factories configuration from the class path, then save them, release the breakpoints, and we can see what we got at this time

Step 4: Find all the ApplicationListener s for the ETA-INF/spring.factories configuration from the class path and work similarly to enter the breakpoint

Step 5: Find the main configuration class with the main method from multiple configuration classes.After this execution, SpringApplication is created

2.2 run method

Paste out the code first

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   //From Class Path META-INF/spring.factories Obtain SpringApplicationRunListeners
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //Callback all acquisitions SpringApplicationRunListener.starting()Method
   listeners.starting();
   try {
      //Encapsulate command line parameters
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
       //Prepare the environment 
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);//Callback when environment creation is complete SpringApplicationRunListener.environmentPrepared();Indicates that the environment is ready
      configureIgnoreBeanInfo(environment);
      //Print Banner chart
      Banner printedBanner = printBanner(environment);
      //Establish ApplicationContext,Decide to create web Of ioc Or normal ioc  
      context = createApplicationContext();
       //Exception Analysis Report
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      //Prepare the context for environment Save to ioc in
      //applyInitializers(): All saved before callback ApplicationContextInitializer Of initialize Method 
      //listeners.contextPrepared(context) 
      //prepareContext Callback all SpringApplicationRunListener Of contextLoaded()
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
       //Refresh Container,ioc Container initialization (if yes) web The application will also create embedded Tomcat)
       //Where to scan, create, load all components,(Configuration class, component, auto-configuration)
      refreshContext(context);       
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      //Be-all SpringApplicationRunListener Callback started Method
      listeners.started(context);
      //from ioc Get all in container ApplicationRunner and CommandLineRunner Callback,
      //ApplicationRunner Callback first, CommandLineRunner Callback
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
       //Be-all SpringApplicationRunListener Callback running Method
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   //Whole SpringBoot Return to startup after application startup completes ioc container
   return context;
}

The preceding code does not need to be analyzed, it is mainly about preparing objects, so let's start with SpringApplicationRunListeners listeners = getRunListeners(args).

The first step is to get the SpringApplicationRunListeners from META-INF/spring.factories under the class path.

This method is similar to the two Get Configuration methods analyzed earlier.

Step 2: Callback all get SpringApplicationRunListener.start() methods.

Step 3: Encapsulate command line parameters.

Step 4: Prepare the environment and call the prepareEnvironment method.

Step 5: Print the Banner diagram (the identification diagram at startup).

Step 6: Create an ApplicationContext and decide whether to create an IOC for the web or a normal ioc.

Step 7: Report of anomaly analysis.

Step 8: Prepare the context and save the environment in ioc. This method needs to be carefully analyzed before we enter this method

Here is an applyInitializers method, which is the initialize method for all ApplicationContextInitializer s saved before the callback

There is also a listeners.contextPrepared(context), which calls back contextPrepared() for all SpringApplicationRunListener s.

The final listeners.contextLoaded(context) is the contextLoaded () that calls back all SpringApplicationRunListener s after the prepareContext runs.

Step 9: Refresh the container, initialize the ioc container (and create an embedded Tomcat if it's a web application), which is where you scan, create, and load all components (configuration classes, components, automatic configuration).

Step 10: All SpringApplicationRunListener callbacks the started method.

Step 11: Retrieve all the ApplicationRunners and CommandLineRunners from the ioc container for callback, ApplicationRunner callback first, CommandLineRunner callback again.

Step 12: All SpringApplicationRunListener callback running methods.

Step 13: Return to the started ioc container after the entire SpringBoot application has started.

This is the whole process of run, and you need to look at the source code yourself for a more detailed understanding.

3. Custom starter

To customize starter, there are two things we need to do: determine dependencies and write automatic configurations.The main thing we need to do is write automatic configuration. We have written some automatic configuration before, mainly to annotate the use of configuration. The main annotations are:

  • @Configuration: Specifies that this class is a configuration class

  • @ConditionalOnXXX: Automatically configure classes to take effect if specified conditions are met

  • @AutoConfigureAfter: Specifies the order in which classes are automatically configured

  • @Bean: Add components to the container

  • @ConfigurationPropertie: Bind related configurations with related xxProperties classes

  • @EnableConfiguration Properties: Make xxProperties valid for inclusion in containers

After writing the auto-configuration classes according to these annotations, we also need to load the auto-configuration classes by configuring them in META-INF/spring.factories when they need to be started. The general principle of the starter is the same, but there is a certain pattern in the actual design of the starter, that is, the starter module is an empty JAR file, providing only auxiliary dependenciesRationally, the auto-configuration module should be redesigned and then referenced by the launcher.This is how Springboot was designed:

There is also a naming rule:

Official Namespace

- Prefix:'spring-boot-starter-'

- Mode: spring-boot-starter-module name

- Examples: spring-boot-starter-web, spring-boot-starter-actuator, spring-boot-starter-jdbc

Custom Namespace

- Suffix: "-spring-boot-starter"

- mode: module-spring-boot-starter

- Example: mybatis-spring-boot-starter

3.1 Create a custom starter

Step 1: Because we need to create two modules, create a new, empty project and then create two modules as modules.

Step 2: Create two more modules, a starter and an automatic configuration module

The specific creation process will not be redundant, that is, the simplest project, remove unwanted files, create the completion structure as follows:

Step 3: Let's first import the auto-configuration module into starter so that the startup module depends on the auto-configuration module

Start module POM file add dependency

<dependencies>
    <!--Introducing an automatic configuration module-->
    <dependency>
        <groupId>com.yuanqinnan-starter</groupId>
        <artifactId>yuanqinnan-springboot-starter-autoconfigurer</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

Full POM file for automatic configuration module:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yuanqinnan-starter</groupId>
    <artifactId>yuanqinnan-springboot-starter-autoconfigurer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--Introduce spring-boot-starter;All starter Basic configuration-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>

So far, the two projects have basically been created, and now we have a simple configuration.

Step 5: Auto-configure code for auto-configure classes

Write a configuration class to configure:

@ConfigurationProperties(prefix = "yuanqinnan.hello")
public class HelloProperties {
    //prefix
    private String prefix;
    //Suffix
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

Write another service

public class HelloService {

    HelloProperties helloProperties;

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }

    public String sayHello(String name) {
        return helloProperties.getPrefix() + "-" + name + helloProperties.getSuffix();
    }
}

Then inject this service into the component:

@Configuration
@ConditionalOnWebApplication //web Application takes effect
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {

    @Autowired
    HelloProperties helloProperties;
    @Bean
    public HelloService helloService(){
        HelloService service = new HelloService();
        service.setHelloProperties(helloProperties);
        return service;
    }
}

This is the last step in our automatic configuration and writing, since SpringBoot read automatic configuration is in the META-INF spring.factories file, so we will also write our automatic configuration class to it

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.yuanqinnan.starter.HelloServiceAutoConfiguration

The final structure is as follows:

At this point, the code and the writing are complete, at which point we will load it into the repository for other projects to reference

3.2 Use custom starter

Create a web project and then introduce dependencies into the project

<!--Introducing Customization starter-->
<dependency>
  <groupId>com.yuanqinnan.starter</groupId>
  <artifactId>yuanqinnan-springboot-starter</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

Add a configuration to the application.properties configuration:

yuanqinnan.hello.prefix=Good morning
yuanqinnan.hello.suffix=good night

Join the test:

@Autowired
HelloService helloService;
​
@Test
public void contextLoads() {
    System.out.println(helloService.sayHello("world"));
}

This completes both custom Starter and reference customization, summarizes the core knowledge of Springboot, and then integrates some advanced scenarios of Springboot, such as caching, messaging, retrieval, distribution, and so on.

Posted by kostasls on Fri, 10 May 2019 15:26:19 -0700