This article has been included in Github 95K + Star’s Java project JavaGuide. JavaGuide project address: github.com/Snailclimb/… .

Author: Miki-byte-1024 & Snailclimb

When asked about SpringBoot, interviewers love to ask: “Tell me about SpringBoot autowiring?” .

I think we can answer this question from the following aspects:

  1. What is SpringBoot autowiring?
  2. How does SpringBoot automate assembly? How to implement load on demand?
  3. How to implement a Starter?

Because of the length, this article does not go into depth. You can also use debug to see the source code of SpringBoot automatic assembly.

preface

Those of you who have used Spring fear being ruled by XML configuration. Even with the introduction of annotation-based configuration later in Spring, we still need explicit configuration in XML or Java when we turn on some Spring features or introduce third-party dependencies.

Let me give you an example. When we write a RestFul Web service without Spring Boot, we also need to do the following configuration first.

@Configuration
public class RESTConfiguration
{
    @Bean
    public View jsonTemplate(a) {
        MappingJackson2JsonView view = new MappingJackson2JsonView();
        view.setPrettyPrint(true);
        return view;
    }

    @Bean
    public ViewResolver viewResolver(a) {
        return newBeanNameViewResolver(); }}Copy the code

spring-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc/ http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.howtodoinjava.demo" />
    <mvc:annotation-driven />

    <! -- JSON Support -->
    <bean name="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
    <bean name="jsonTemplate" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>

</beans>
Copy the code

However, for the Spring Boot project, we only need to add related dependencies without configuration, by starting the main method below.

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

In addition, we can use the global configuration file application.properties or application.yml of Spring Boot to set the project, such as changing the port number, configuring the JPA properties, etc.

Why is Spring Boot so sour to use? This is thanks to automatic assembly. Autowiring is at the heart of Spring Boot, but what exactly is autowiring?

What is SpringBoot autowiring?

When we talk about autowiring these days, it’s usually associated with Spring Boot. However, the Spring Framework has already implemented this functionality. Spring Boot just builds on that and optimizes it further by means of SPI.

SpringBoot defines a set of interface specifications that state: When SpringBoot starts, it scans the meta-INF/Spring. factories file in the external reference JAR and loads the configured type information into the Spring container. And performs various operations defined in the class. External jars can install their own functionality into SpringBoot only by following the standards defined by SpringBoot.

Without Spring Boot, if we need to introduce third-party dependencies, we need to manually configure them, which is very troublesome. However, in Spring Boot, we simply introduce a starter. For example, if you want to use Redis in your project, you can simply introduce the corresponding starter into your project.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code

With the introduction of Starter, we can use functionality provided by third-party components with a few annotations and a few simple configurations.

In my opinion, autowiring can be simply defined as implementing a feature with the help of Spring Boot through annotations or some simple configuration.

How does SpringBoot automate assembly?

Let’s take a look at SpringBoot’s core annotation, SpringBootApplication.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
<1.>@SpringBootConfiguration
<2.>@ComponentScan
<3.>@EnableAutoConfiguration
public @interface SpringBootApplication {

}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // It is actually a configuration class
public @interface SpringBootConfiguration {
}
Copy the code

You can probably think of @SpringBootApplication as a collection of @Configuration, @EnableAutoConfiguration, and @ComponentScan annotations. According to The SpringBoot website, the three annotations are:

  • @enableAutoConfiguration: Enables the automatic configuration of SpringBoot

  • @Configuration: Allows registering additional beans or importing additional Configuration classes in context

  • @ComponentScan: Scans beans annotated by @Component (@Service,@Controller). By default, annotations scan all classes in the package in which the class is started. As shown in the figure below, will eliminate TypeExcludeFilter and AutoConfigurationExcludeFilter container.

Let’s start with @enableAutoConfiguration, an important annotation for implementing autowire.

EnableAutoConfiguration: Core annotation to implement autowiring

EnableAutoConfiguration is just a simple annotation, the realization of the function of automatic assembly core is actually through AutoConfigurationImportSelector class.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // Registers the desired components from the main package into the container
@Import({AutoConfigurationImportSelector.class}) // Load the auto-assembly class xxxAutoconfiguration
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<? >[] exclude()default {};

    String[] excludeName() default {};
}
Copy the code

We now focus on analysis the AutoConfigurationImportSelector class how do?

AutoConfigurationImportSelector: load automatic assembly class

AutoConfigurationImportSelector class inheritance system is as follows:

public class AutoConfigurationImportSelector implements DeferredImportSelector.BeanClassLoaderAware.ResourceLoaderAware.BeanFactoryAware.EnvironmentAware.Ordered {}public interface DeferredImportSelector extends ImportSelector {}public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}
Copy the code

It can be seen that AutoConfigurationImportSelector class implements the ImportSelector interface, it implements this interface of selectImports method, this method is mainly used to obtain all conform to the conditions of the fully qualified class name of a class, These classes need to be loaded into the IoC container.

private static final String[] NO_IMPORTS = new String[0];

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // <1>. Check whether the automatic assembly switch is turned on
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
          //<2>. Get all beans that need to be assembled
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }}Copy the code

Here we need to focus on getAutoConfigurationEntry () method, this method is mainly responsible for load automatic configuration of the class.

The method call chain is as follows:

Now we combine getAutoConfigurationEntry (source) to analyze in detail:

private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();

AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        / / < 1 >.
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            / / < 2 >.
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            / / < 3 >.
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            / / < 4 >.
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return newAutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); }}Copy the code

Step 1:

Check whether the automatic assembly switch is on. The default spring. The boot. Enableautoconfiguration = true, in the application. The properties and application. The yml in Settings

Step 2:

Used to get exclude and excludeName in the EnableAutoConfiguration annotation.

Step 3

Get all the configuration classes that need to be auto-assembled and read meta-INF/Spring.Factories

spring-boot/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
Copy the code

The following figure shows that the configuration content of this file is read. XXXAutoConfiguration is used to load components on demand.

Not only meta-INF/Spring. factories under this dependency are read, but all meta-INF/Spring. factories under the Spring Boot Starter are read.

So, you can clearly see that the Druid database connection pool’s Spring Boot Starter creates meta-INF/Spring.Factories.

This step is essential if we want to create a Spring Boot Starter ourselves.

Step 4:

At this point your interviewer will probably ask you, “Do you need to load all the configurations in spring.factories every time you start?” .

Obviously, this is not realistic. If we debug later you’ll see that the value of Configurations is smaller.

The class is not valid until all the conditions in @conditionalonxxx have been met.

@Configuration
// Check whether the associated classes RabbitTemplate and Channel exist
// Load only if it exists
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {}Copy the code

Those interested can take a closer look at Spring Boot’s conditional annotations

  • @ConditionalOnBean: when there are specified beans in the container
  • @ConditionalOnMissingBean: when there is no specified Bean in the container
  • @ConditionalOnSingleCandidate: Specifies the preferred Bean when the specified Bean has only one in the container or more than one
  • @ConditionalOnClass: When there is a specified class in the classpath
  • @ConditionalOnMissingClass: When no class is specified on the classpath
  • @ConditionalOnProperty: Indicates whether the specified attribute has the specified value
  • @ConditionalOnResource: Specifies whether the classpath has a value
  • @ConditionalOnExpression: Based on the SpEL expression as the judgment condition
  • @ConditionalOnJava: Based on the Java version
  • @ConditionalOnJndi: difference at the specified location in the presence of JNDI
  • @ConditionalOnNotWebApplication: The current project is not a Web project
  • @ConditionalOnWebApplication: The current project is a Web project

How to implement a Starter

It’s time to start with a starter to create a custom thread pool

Step 1, create the Threadpool-spring-boot-starter project

Second, introduce Spring Boot-related dependencies

The third step, create ThreadPoolAutoConfiguration

Create meta-INF /spring.factories under the Resources package of the Threadpool-spring-boot-starter project

Finally, the new project introduces threadpool-spring-boot-starter

Test passed!!

conclusion

Spring Boot uses @enableAutoConfiguration to enable auto-assembly. SpringFactoriesLoader loads the auto-configuration classes in Meta-INF/Spring. factories to enable auto-assembly. The automatic configuration class is actually a configuration class loaded on demand through @Conditional, and the spring-boot-starter-xxx package must be introduced to make it effective

The article ends with

I am Guide brother, a Java backend development, will be a little front-end, free teenager. See you next time! WeChat search”JavaGuide“Reply”The interview assault“Get 4 original PDFS that I organized