Brief introduction:

  1. Spring Boot is an entirely new framework in the Spring family, provided by the Pivotal team, to simplify the creation and development of Spring applications, It can also be said that Spring Boot can simplify our previous development process using SpringMVC+Spring+Mybatis framework.
  2. In the past, when we used SpringMVC+Spring+Mybatis framework for development, we built and integrated the three frameworks. We needed to do good work, such as configuring Web. XML, configuring Spring, configuring Mybatis, and integrating them together. The Spring Boot framework revolutionized the development process by eliminating tedious XML configuration and simplifying our development process with a lot of default configuration.
  3. So using Spring Boot makes it very easy and fast to create applications based on the Spring framework. It makes coding easier, configuration easier, deployment easier, and monitoring easier.

I. SpringBoot features:

  • Create stand-alone Spring applications
  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
  • Provide opinionated ‘starter’ dependencies to simplify your build configuration
  • Automatically configure Spring and 3rd party libraries whenever possible
  • Provide production-ready features such as metrics, health checks and externalized configuration
  • Absolutely no code generation and no requirement for XML configuration

Two, SpringBoot version:

CURRENT: indicates the latest version

GA:General Availability, official release. This version is recommended. In foreign countries, we use GA to describe the release version.

PRE: Preview version, internal beta version. It is mainly used for developers and testers to test and find bugs. It is not recommended to use;

SNAPSHOT: SNAPSHOT version. It is stable and is still being improved.

“SpringBoot Application Starters”

Springboot provides some mainstream integration packages as shown below, but some non-mainstream integration packages are provided by the integrator itself. For example, Springboot and Dubbo integration packages are provided by Apache.

Iv. Dubbo Background:

Single application Architecture

When site traffic is low, only one application is needed to deploy all functions together to reduce deployment nodes and costs. At this point, data access frameworks (ORM) that simplify the work of adding, deleting, modifying and reviewing are key.

Vertical application Architecture

When the volume of traffic gradually increases, the acceleration caused by the increase of a single application machine is getting smaller and smaller. The application is divided into several unrelated applications to improve efficiency. At this point, a Web framework (MVC) for accelerating front-end page development is key.

Distributed Service Architecture

With the increasing number of vertical applications, the interaction between applications is inevitable. Core businesses are extracted as independent services, gradually forming a stable service center, so that front-end applications can respond to changing market demands more quickly. At this point, the distributed Services framework (RPC) for business reuse and integration is key.

Mobile Computing Architecture

As the number of services increases, problems such as capacity evaluation and waste of small service resources gradually emerge. In this case, a scheduling center needs to be added to manage cluster capacity in real time based on access pressure to improve cluster utilization. At this point, a resource scheduling and Governance center (SOA) for improving machine utilization is key.

SpringBoot integration Dubbo

Reference document 1: com.alibaba

Reference 2: Apache Dubbo

SpringBoot integration dubbo version requirements:

Dubbo Spring Boot Dubbo Spring Boot 0.2.1.RELEASE 2.6.5+ 2.2.x 0.1.2.RELEASE 2.6.5+ 1.x

Step 1: Create a project boot-dubbo to introduce basic configuration in the parent module pom.xml

<? The 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 > < groupId > com. Clinks < / groupId > < artifactId > boot - dubbo - parent < / artifactId > < version > 1.0 - the SNAPSHOT < / version > <! <packaging> poM </packaging> <! -- Step 1: Add parent to Springboot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.9.RELEASE</version> </parent> <modules> <module>.. /boot-dubbo-dl</module> <module>.. /boot-dubbo-service</module> <module>.. /boot-dubbo-web</module> </modules> <! -- Version properties --> <properties> <! RELEASE</ dubo.boot.version > < dubo.version >2.6.5</ dubo.version > </properties> </project>Copy the code

Step 2: Create three submodules: boot-Dubbo-API, boot-Dubbo-provider, and boot-dubbo-web

Boot-dubo-api as an interface module, into jar package can be referenced by the other two modules, all service interface declarations can be written in this module;

Boot-dubbo-provider serves as a service provider. The interface implementation class can be written in the module and registered in the registry for other modules to call.

Boot-dubo-web as a service consumer, RPC can remotely invoke the service.

Step 3: Boot-dubbo-dl part pom.xml:

<? The 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" > The < modelVersion > 4.0.0 < / modelVersion > < artifactId > boot - dubbo - dl < / artifactId > < packaging > jar < / packaging > < the parent > < artifactId > boot - dubbo - parent < / artifactId > < groupId > com. Clinks < / groupId > < version > 1.0 - the SNAPSHOT < / version > <relativePath>.. /boot-dubbo-parent/pom.xml</relativePath> </parent> <dependencies> <! -- jar dependency --> <dependency> <groupId>com.clinks</groupId> <artifactId> boot-dubo-service </artifactId> < version > 1.0 - the SNAPSHOT < / version > < / dependency > <! Starter dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <! -- springboot-dubbo dependency --> <dependency> <groupId>com.alibaba. <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo.boot.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <! Clients </groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> </dependencies> </project>Copy the code

Step 4. Configuration file

---- XML ---- <dubbo:application name="agent-hr-dl"/> <dubbo:registry address="${dubo. address}" username="test" password="${redis.password}"/> <dubbo:consumer retries="0" filter="cat_filter"/> <dubbo:service Interface = "com. Clinks. Agent. Hr. Service. IAgentKpiService" ref = "agentKpiServiceImpl" / > -- -- annotation way Dubbo. Application. Name = dubbo - consumer - the annotation dubbo. Registry. Address = redis: / / 139.196.28.237:6641 dubbo.registry.username=test dubbo.registry.password=Toodc600 dubbo.consumer.timeout=3000 dubbo.consumer.registries=0 - javaConfig wayCopy the code

Step 5: The core startup class Application

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

Step 6: Boot-Dubo-web part pom.xml:

<? The 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 > < the parent > < groupId > com. Clinks < / groupId > < artifactId > boot - dubbo - parent < / artifactId > < version > 1.0 - the SNAPSHOT < / version > < relativePath >). /boot-dubbo-parent</relativePath> </parent> <groupId>com.clinks</groupId> <artifactId>boot-dubbo-web</artifactId> < version > 1.0 - the SNAPSHOT < / version > < dependencies > <! -- jar dependency --> <dependency> <groupId>com.clinks</groupId> <artifactId> boot-dubo-service </artifactId> < version > 1.0 - the SNAPSHOT < / version > < / dependency > <! Starter dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <! -- springboot-dubbo dependency --> <dependency> <groupId>com.alibaba. <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo.boot.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <! -- Web module --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId> Spring-boot-starter -web</artifactId> </dependency> <! --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> < version > 4.3.7. RELEASE < / version > < / dependency > <! Clients </groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> </dependencies> </project>Copy the code

Six, the core notes

Note 1: @SpringBootApplication: **

Combination notes:

@configuration: is a class annotation that indicates that this is a Configuration class and declares the startup class as a Configuration class

Commonly used with @bean

@ComponentScan: Specifies the scan package

By default, Springboot scans the same package and annotations under its subpackages, such as @Controller,@Service, and @Configuration

@enableAutoConfiguration: Enable automatic configuration of the Spring application context

Simple principle:

Note 2: @enabledubbo

  1. The @enableDubbo annotation is a convenient way to combine @enableDubboConfig and @DubBoComponentScan. Associated with the annotation driver is @DubboComponentScan.
  2. With @enableDubbo you can specify the package name (via scanBasePackages), Or the specified class (via scanBasePackageClasses) can scan for Dubbo’s Service provider (labeled @service) and Dubbo’s Service consumer (labeled Reference).
  3. After scanning the service providers and consumers of Dubbo, they are assembled and initialized accordingly, and finally the service is exposed or referenced.

Note 3: @service

You can further customize Dubbo’s Service providers by providing attributes on @service:

package org.apache.dubbo.config.annotation; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) // #1 @Inherited public @interface Service {  Class<? > interfaceClass() default void.class; // #2 String interfaceName() default ""; // #3 String version() default ""; // #4 String group() default ""; // #5 boolean export() default true; // #6 boolean register() default true; // #7 String application() default ""; // #8 String module() default ""; // #9 String provider() default ""; // #10 String[] protocol() default {}; // #11 String monitor() default ""; // #12 String[] registry() default {}; / / # 13}Copy the code

Some of the most important ones are:

  1. @service can only be defined on a class and represents a concrete implementation of a Service
  2. InterfaceClass: Class that specifies the interface implemented by the service provider
  3. InterfaceName: Specifies the class name of the interface implemented by the service provider
  4. Version: specifies the version number of the service
  5. Group: specifies the service group
  6. Export: Indicates whether to expose the service
  7. Registry: Whether to register a service with a registry
  8. Application: indicates application configuration
  9. Module: Module configuration
  10. Provider: configures the service provider
  11. Protocol: indicates protocol configuration
  12. Monitor: monitoring center configuration
  13. Registry: Registry configuration

Note 4: @Reference

Dubbo service consumers can be further customized using the attributes provided at @Reference:

package org.apache.dubbo.config.annotation; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) // #1 public @interface Reference { Class<? > interfaceClass() default void.class; // #2 String interfaceName() default ""; // #3 String version() default ""; // #4 String group() default ""; // #5 String url() default ""; // #6 String application() default ""; // #7 String module() default ""; // #8 String consumer() default ""; // #9 String protocol() default ""; // #10 String monitor() default ""; // #11 String[] registry() default {}; / / # 12}Copy the code

Some of the most important ones are:

  1. @Reference can be defined on a field in a class, on a method, or even on another annotation that represents a Reference to a service. Usually @reference is defined on a field
  2. InterfaceClass: Class that specifies the interface of the service
  3. InterfaceName: Specifies the class name of the interface of the service
  4. Version: specifies the version number of the service
  5. Group: specifies the service group
  6. Url: Bypasses the registry by specifying the URL of the service provider
  7. Application: indicates application configuration
  8. Module: Module configuration
  9. Consumer: service consumer configuration
  10. Protocol: indicates protocol configuration
  11. Monitor: monitoring center configuration
  12. Registry: Registry configuration

SpringBoot source code analysis

Startup process:

Create the SpringApplication object

initialize(sources); Private void initialize(Object[] sources) {// Save the main configuration class if (sources! = null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } // Decide if the current application is a Web application this.webenvironment = deduceWebEnvironment(); / / from the classpath find META ‐ INF/spring. All ApplicationContextInitializer factories configuration; And then save up setInitializers ((Collection) getSpringFactoriesInstances (ApplicationContextInitializer. Class); // Find all ApplicationListener setListeners(Collection) in the ETA‐INF/ spring-factories configuration getSpringFactoriesInstances(ApplicationListener.class)); / / found in the multiple configuration class is the main method of the main configuration class enclosing mainApplicationClass = deduceMainApplicationClass (); }Copy the code

2. Run the run() method

public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); / / get SpringApplicationRunListeners; From the classpath META ‐ INF/spring. Factories SpringApplicationRunListeners listeners = getRunListeners (args); / / callback all get SpringApplicationRunListener. Starting () method of the listeners. The starting (); Try {/ / encapsulation command line parameters ApplicationArguments ApplicationArguments = new DefaultApplicationArguments (args); / / prepared environment ConfigurableEnvironment environment = prepareEnvironment (listeners, applicationArguments); / / create environment after the completion of the callback SpringApplicationRunListener environmentPrepared (); PrintedBanner = printBanner(environment); // Create ApplicationContext; Decide whether to create a Web IOC context or a normal IOC context = createApplicationContext(); analyzers = new FailureAnalyzers(context); // Prepare the context; Save the environment to ioc; And applyInitializers (); / / applyInitializers () : Callback previously saved all ApplicationContextInitializer initialize method / / callback all SpringApplicationRunListener contextPrepared (); prepareContext(context, environment, listeners, applicationArguments, printedBanner); / / prepareContext completion callback after all SpringApplicationRunListener contextLoaded (); // Refresh the container; The IOC container is initialized (and embedded Tomcat is also created for Web applications); Spring annotations // the place to scan, create, and load all components; (Configure classes, components, automatically configure) refreshContext(context); Fetch all ApplicationRunner and CommandLineRunner from the IOC container for the callback. CommandLineRunner then calls afterRefresh(Context, applicationArguments); / / all the listeners SpringApplicationRunListener callback finished method. The finished (context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch);  } // Return the ioc container after the SpringBoot application is started; return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); }}Copy the code

3. Life cycle process:

/ / use SpringFactoriesLoader to look for in the application of the classpath and load all available ApplicationContextInitializer. // Use SpringFactoriesLoader to find and load all available ApplicationListeners in your application's classpath. When initializing SpringApplication collects callback interface / / SpringFactoriesLoader SpringApplicationRunListener can find and load SpringApplicationRunListeners listeners = this.getRunListeners(args); // note that SpringBoot has started executing listeners. Starting (); // Create and configure the SpringBoot environment this.prepareEnvironment(Listeners); { listeners.environmentPrepared(); / / explain SpringBoot environment has been configured} / / create the Spring container context = this. CreateApplicationContext () / / configure the Spring container context this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); { this.applyInitializers(context); { this.getInitializers() .initialize(context) } listeners.contextPrepared(context); {this.listeners().contextprepared (context) // called after the ApplicationContext is created and ready, but before the source is loaded. } listeners.contextLoaded(context); Notice ().contextloaded (context) // Call}} after loading the application context but before refreshing the Spring container, Generate bean this.refreshContext(context) by reflection; this.afterRefresh(); // Context refreshed, application started, but commandLinerunner and ApplicationRunner Listeners. Started (context) have not yet been called; // Find if CommandLineRunner is registered in the current ApplicationContext, and if so, iterate through them. this.callRunners(context, applicationArguments); { ApplicationRunner; CommandLineRunner; }Copy the code

Automatic SpringBoot configuration

SpringBoot for us to do automatic configuration, it is convenient and fast, but for beginners, if you do not understand the internal startup principle of SpringBoot, it will inevitably suffer.

Entrance class

Any Spring Boot project we develop will use the following Boot classes

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

As you can see from the code above, the Annotation definition (@SpringBootApplication) and the class definition (springApplication.run) shine the most, so to demythize SpringBoot, we need to start with these two.

The secret behind SpringBootApplication

@target (elementType.type) // Scope of annotations, where TYPE is used to describe classes, interfaces (including package annotation types), or enum statements @Retention(retentionPolicy.runtime) // Lifecycle of annotations, @documented // Indicates that this annotation should be Documented by Javadoc. @inherited // A child class can inherit this annotation @enableAutoConfiguration // Enable springBoot annotation function, one of the four magic tools of SpringBoot, @componentScan (excludeFilters = {) @filter (type = filterType.custom) @componentScan (excludeFilters = {) @filter (type = filterType.custom, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { ... }Copy the code

Although the definition uses multiple annotations to annotate the original information, there are actually only three annotations that matter:

  • @configuration (@springBootConfiguration)
  • @EnableAutoConfiguration
  • @ComponentScan

So, if we use the following SpringBoot boot class, the whole SpringBoot application will still function as the previous boot class:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
Copy the code

It’s kind of tiring to write these three at a time, so it’s a little bit easier to write at @SpringBootApplication. So let’s go through each of these annotations.

@Configuration

@Configuration is the same @Configuration that is used in the Configuration class of the Spring Ioc container in JavaConfig form. The SpringBoot community recommends using the Javaconfig-based Configuration form. The startup class labeled @Configuration is itself a Configuration class for the IoC container.

A few simple examples to review the differences between XML and Config:

    • The xmL-based configuration at the presentation level looks like this:

Javaconfig-based configuration works like this:

@configuration public class MockConfiguration{//bean definition}Copy the code

Any Java class definition labeled @Configuration is a JavaConfig Configuration class.

    • The xmL-based configuration at the registration bean definition level looks like this:

      .

The Javaconfig.based configuration looks like this:

@Configuration public class MockConfiguration{ @Bean public MockService mockService(){ return new MockServiceImpl(); }}Copy the code

The return value of any method labeled @bean is registered with Spring’s IoC container as a Bean definition, and the method name defaults to the id of the Bean definition.

    • To express bean-to-bean dependencies, in XML form we typically do this:

The Javaconfig.based configuration looks like this:

@Configuration
public class MockConfiguration{
    @Bean
    public MockService mockService(){
        return new MockServiceImpl(dependencyService());
    }
    
    @Bean
    public DependencyService dependencyService(){
        return new DependencyServiceImpl();
    }java
}
Copy the code

If the definition of a bean depends on another bean, you can simply call the dependent bean creation method in the corresponding JavaConfig class.

@ComponentScan

The @ComponentScan annotation is very important in Spring. It corresponds to elements in the XML configuration. The @ComponentScan annotation automatically scans and loads qualified components (@Component, @Repository, etc.) or bean definitions. These bean definitions are eventually loaded into the IoC container.

We can fine-grained customize the scope that @ComponentScan automatically scans through properties like basePackages. If not specified, the default Spring framework implementation scans from the package of the class in which @ComponentScan is declared.

@EnableAutoConfiguration

I think @enableAutoConfiguration is the Annotation that’s the most important, so I’m going to leave it at the end. Do you remember the various Annotation definitions that Spring provides that start with @enable? For example, @Enablescheduling, @Enablecaching, @enablembeanexport, etc. The concept of @EnableAutoConfiguration is in fact consistent with the way of doing things. A brief summary is that with the support of @import, Collect and register bean definitions associated with specific scenarios.

  • @enablescheduling loads Spring scheduling-framework related bean definitions into the IoC container via @import.
  • @enablembeanExport loads jMX-related bean definitions into the IoC container via @import.

@enableAutoConfiguration loads all auto-configured bean definitions into the IoC container with the help of @import, and that’s it!

As a composite Annotation, @enableAutoConfiguration defines the key information as follows:

@SuppressWarnings("deprecation") @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {... }Copy the code

Two important notes:

  • @autoConfigurationPackage: Automatic configuration package
  • @import: Import automatically configured components

AutoConfigurationPackage comments:

1 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { 2 3 @Override 4 public void registerBeanDefinitions(AnnotationMetadata metadata, 5 BeanDefinitionRegistry registry) { 6 register(registry, new PackageImport(metadata).getPackageName()); 7}Copy the code

It actually registers the definition of a Bean.

New PackageImport(metadata).getPackagename (), which returns package components that are * siblings and * children of the current main program class.

Example: DemoApplication is the parent of the demo package, but demo2 is the parent of the Demo package

That is, DemoApplication does not load Demo2 in the Bean that DemoApplication starts loading, which is why we put DemoApplication at the highest level of the project.

Import (AutoConfigurationImportSelector. Class) comments:

Can see from figure AutoConfigurationImportSelector inherited DeferredImportSelector inherited ImportSelector * * * *

ImportSelector has a method called selectImports.

@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (! isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); }Copy the code

Public static final String FACTORIES_RESOURCE_LOCATION = “meta-INF /spring.factories”; External files. This external file has a lot of auto-configured classes. As follows:

One of the most critical is @ Import (EnableAutoConfigurationImportSelector. Class), with the aid of EnableAutoConfigurationImportSelector, @enableAutoConfiguration helps SpringBoot applications load all eligible @Configuration configurations into the IoC container currently created and used by SpringBoot. Just like an octopus.

Automatic configuration hero: SpringFactoriesLoader details

With the support of SpringFactoriesLoader, @enableAutoConfiguration can be automatically configured.

The SpringFactoriesLoader is a proprietary extension of the Spring framework that loads configurations from a specified configuration file, meta-INF/Spring.Factories.

public abstract class SpringFactoriesLoader {
    //...
    public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
        ...
    }


    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        ....
    }
}
Copy the code

Used in conjunction with @enableAutoConfiguration, it provides more of a configuration lookup feature, According to the full name of the class of the @ EnableAutoConfiguration org. Springframework. Boot. Autoconfigure. EnableAutoConfiguration as a lookup Key, obtain corresponding to a set of @ Configura Tion class

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

So the @enableAutoConfiguration magic knight becomes: 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.

SpringBoot startup principle is basically finished, in order to facilitate memory, I draw a picture according to the above analysis

Explore the SpringApplication execution process in depth

The implementation of The Run method of SpringApplication is the main route of our journey, and the main process of this method can be summarized as follows:

1) If we use the static run method of SpringApplication, the method will first create an instance of the SpringApplication object, and then call the instance method. When a SpringApplication instance is initialized, it does several things ahead of time:

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
        return new SpringApplication(sources).run(args);
    }
Copy the code
  • According to the characters of the classpath inside whether there is a certain class (org. Springframework. Web. Context. ConfigurableWebApplicationContext) to decide whether it should be to create a web Application using the Application The Context type.

  • Using SpringFactoriesLoader to look for in the application of the classpath and load all available ApplicationContextInitializer.

  • Use the SpringFactoriesLoader to find and load all available ApplicationListeners in your application’s classpath.

  • Infer and set the definition class for the main method.

    @SuppressWarnings({ “unchecked”, “rawtypes” }) private void initialize(Object[] sources) { if (sources ! = null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }

2) After the SpringApplication instance is initialized and set, the logic of the run method is executed. First traversal perform all through SpringFactoriesLoader SpringApplicationRunListener can find and load. Call them started () method, tell these SpringApplicationRunListener, “hey, SpringBoot application to start cough up!” .

public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); analyzers = new FailureAnalyzers(context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); &emsp; &emsp; &emsp; &emsp; &emsp; &emsp; &emsp; // Core point: The springBoot boot flag is printed until refreshContext(context) is enabled on server.port; afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch);  } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); }}Copy the code

3) Create and configure the Environment to be used by the current Spring Boot application (including PropertySource and Profile to be used).

private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment  = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (! this.webEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } return environment; }Copy the code

4) iterates through all SpringApplicationRunListener call environmentPrepared () method, told them: “the current SpringBoot applications use the Environment ready!” .

public void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); }}Copy the code

5) If the SpringApplication showBanner property is set to true, the banner is printed.

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

6) Determine what type of ApplicationContext should be created for the current SpringBoot application based on whether the applicationContextClass type has been explicitly set by the user and the inferred results of the initialization phase. Then depending on the conditions, decide whether to add ShutdownHook, decide whether to use a custom BeanNameGenerator, decide whether to use a custom ResourceLoader, and of course, most importantly, Set the previously prepared Environment to be used by the created ApplicationContext.

7) After the ApplicationContext is created, The SpringApplication will use the Spring-FactoriesLoader again, Find and load all available ApplicationContext-Initializers in your classpath, And call these ApplicationContextInitializer initialize traversal (applicationContext) method to already created good applicationContext for further processing.

@SuppressWarnings({ "rawtypes", "unchecked" })
    protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                    initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
Copy the code

8) iterates through all SpringApplicationRunListener call contextPrepared () method.

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner ! = null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // Load the sources Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); listeners.contextLoaded(context); }Copy the code

9) The most essential step is to load all the configuration obtained via @enableAutoConfiguration and other forms of IoC container configuration into the prepared ApplicationContext.

private void prepareAnalyzer(ConfigurableApplicationContext context, FailureAnalyzer analyzer) { if (analyzer instanceof BeanFactoryAware) { ((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory()); }}Copy the code

10) iterates through all SpringApplicationRunListener call contextLoaded () method.

public void contextLoaded(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.contextLoaded(context); }}Copy the code

11) Call the Refresh () method of the ApplicationContext to complete the final procedure available to the IoC container.

private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
Copy the code

12) Find if CommandLineRunner is registered in the current ApplicationContext, and if so, iterate through them.

private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); }}}Copy the code

13) under normal circumstances, through implementing SpringApplicationRunListener finished () method, (if the abnormal, the whole process is still calling all SpringApplicationRunListener finished () method, Except in this case, the exception message is passed in as well.)

After removing the event notification point, the whole process is as follows:

public void finished(ConfigurableApplicationContext context, Throwable exception) { for (SpringApplicationRunListener listener : this.listeners) { callFinishedListener(listener, context, exception); }}Copy the code

Custom event listening mechanism

SpringApplicationRunListener interface specifies SpringBoot lifecycle, in each life cycle events corresponding, calls the actual ApplicationListener class. Through the analysis of SpringApplicationRunListener, can also be understanding of the entire launch process of SpringBoot there will be a great help.

Public interface SpringApplicationRunListener {/ / when the run method starts immediately call for the first time. Can be used for very early initialization. void starting(); // Called after the environment is ready, but before ApplicationContext is created. void environmentPrepared(ConfigurableEnvironment environment); // Called after ApplicationContext is created and ready, but before the source is loaded. void contextPrepared(ConfigurableApplicationContext context); / / but after loading the application context to refresh before calling void contextLoaded (ConfigurableApplicationContext context). // The context is refreshed and the application is started, but CommandLinerunner and ApplicationRunner have not yet been called. void started(ConfigurableApplicationContext context); // called immediately before the run method completes, when the application context is refreshed, and all CommandLinerunner and ApplicationRunner are called. / / 2.0 is void running (ConfigurableApplicationContext context). // Called when a failure occurs while running the application. 2.0 have a void failed (ConfigurableApplicationContext context, Throwable exception). } ApplicationStartingEvent is sent at the start of a run but before any processing, except the registration of listeners and initializers. ApplicationEnvironmentPreparedEvent in context to use environment known but send before creating context. ApplicationPreparedEvent will be sent before the refresh starts, but after the bean definition is loaded. ApplicationStartedEvent is sent after the context is refreshed, but before any application and command-line execution programs are invoked. ApplicationreadyEvent is emitted after any application and command line run program is called. It indicates that the application is ready to service the request. ApplicationFailedEvent If an exception occurs during startup.Copy the code

ApplicationContextInitializer

package web.listeners; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; /** * @author dongmei.tan * @date 2019/5/26 17:09 */ public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { System.out.println("HelloApplicationContextInitializer... initialize..." ); }}Copy the code

SpringApplicationRunListener

package web.listeners; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplicationRunListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; /** * @author dongmei.tan * @date 2019/5/26 17:16 */ public class HelloSpringApplicationRunListener implements SpringApplicationRunListener { public HelloSpringApplicationRunListener(SpringApplication application, String[] args) { } @Override public void starting() { System.out.println("SpringApplicationRunListener... starting.. "); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { System.out.println("SpringApplicationRunListener... environmentPrepared.. ; ==environment" + environment); } @Override public void contextPrepared(ConfigurableApplicationContext context) { System.out.println("SpringApplicationRunListener... contextPrepared.. ==> " + context); } @Override public void contextLoaded(ConfigurableApplicationContext context) { System.out.println("SpringApplicationRunListener... starting.. ==> " + context); } @Override public void started(ConfigurableApplicationContext context) { System.out.println("SpringApplicationRunListener... started.. ==> " + context); } @Override public void running(ConfigurableApplicationContext context) { System.out.println("SpringApplicationRunListener... running.. ==> " + context); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { System.out.println("SpringApplicationRunListener... failed.. ==> " + context); }}Copy the code

Configure (meta-INF /spring.factories)

org.springframework.context.ApplicationContextInitializer=\
web.listeners.HelloApplicationContextInitializer

# Application Listeners
org.springframework.boot.SpringApplicationRunListener=\
web.listeners.HelloSpringApplicationRunListener
Copy the code

Just put it in an IOC container

ApplicationRunner

CommandLineRunner

If you need to run some specific code after SpringApplication is started, you can implement the ApplicationRunner or CommandLineRunner interface. Both interfaces work the same way and provide a separate Run method in springApplication.run (...) Call before completion. The commandLineRunner interface provides access to application parameters in the form of a simple array of strings, while applicationRunner uses the applicationArguments interface discussed earlier. The following example shows commandLinerunner: Package web.listeners; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; /** * @author dongmei.tan * @date 2019/5/26 17:21 */ @Component public class HelloApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner... run.." ); } } package web.listeners; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; /** * @author dongmei.tan * @date 2019/5/26 17:22 */ @Component public class HelloCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("CommandLineRunner... run..." ); }}Copy the code