Java framework learning 04 (SpringBoot automatic assembly principle)

1. What is SpringBoot automatic assembly?
2. How does SpringBoot implement automatic assembly? How to achieve on-demand loading?
3. How to implement a Starter?

foreword

Friends who have used Spring must have the fear of being ruled by XML configuration. Even though Spring introduces annotation-based configuration later, when we enable certain Spring features or introduce third-party dependencies, we still need to use XML or Java for explicit configuration.

for example. When we write a RestFul web service without Spring Boot, we first need to configure it as follows.

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

    @Bean
    public ViewResolver viewResolver() {
    
    
        return new BeanNameViewResolver();
    }
}

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>

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

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

Moreover, we can set the project through the global configuration file of Spring Boot application.properties, such as changing the port number, configuring JPA properties, and so on. Why is Spring Boot so cool to use? This is thanks to its autowiring. Automatic assembly can be said to be the core of Spring Boot, so what is automatic assembly?application.yml

1. What is SpringBoot automatic assembly?

Now when it comes to automatic assembly, it is generally associated with Spring Boot. However, in fact, Spring Framework has already realized this function. Spring Boot is only based on it, and through SPIthe way, it has been further optimized.

SpringBoot defines a set of interface specifications, which stipulate that: SpringBoot will scan the META-INF/spring.factories file in the externally referenced jar package at startup, and load the type information configured in the file into the Spring container (here involves JVM class loading mechanism and Spring's container knowledge), and perform various operations defined in the class. For external jars, you only need to install your own functions into SpringBoot according to the standards defined by SpringBoot.

Without Spring Boot, if we need to introduce third-party dependencies, manual configuration is required, which is very troublesome. However, in Spring Boot, we can directly introduce one starter. For example, if you want to use redis in the project, you can directly import the corresponding starter in the project.

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

After introducing the starter, we can use the functions provided by third-party components through a few annotations and some simple configurations.

Automatic assembly can be simply understood as: through annotations or some simple configurations, a certain function can be realized with the help of Spring Boot.

2. How does SpringBoot implement automatic assembly?

Let's first look at the core annotations of SpringBoot SpringBootApplication.

b

It can probably be regarded @SpringBootApplicationas @Configuration、@EnableAutoConfiguration、@ComponentScana collection of annotations. According to the SpringBoot official website, the functions of these three annotations are:

  • @EnableAutoConfiguration: Enable SpringBoot's automatic configuration mechanism
  • @Configuration: Allows to register additional beans or import other configuration classes in the context
  • @ComponentScan: Scan beans annotated by @Component (@Service, @Controller). The annotation will scan all classes under the package where the startup class is located by default. You can customize not to scan some beans. As shown in the image below, and are excluded from the TypeExcludeFiltercontainer AutoConfigurationExcludeFilter.

insert image description here

@EnableAutoConfigurationIt is an important annotation to realize automatic assembly. We start with this annotation.

3. @EnableAutoConfiguration: The core annotation for automatic assembly

EnableAutoConfigurationJust a simple note, the realization of the core function of automatic assembly is actually through AutoConfigurationImportSelectorthe class.

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //作用:将main包下的所有组件注册到容器中
@Import({
    
    AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
    
    
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {
    
    };

    String[] excludeName() default {
    
    };
}

Now focus on analyzing AutoConfigurationImportSelectorwhat the class does?

AutoConfigurationImportSelector: load autowiring class

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

It can be seen that the AutoConfigurationImportSelector class implements ImportSelectorthe interface, and also implements selectImportsthe methods in this interface. This method is mainly used to obtain the fully qualified class names of all eligible classes that need to be loaded into the IoC container .

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

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
    
        // <1>.判断自动装配开关是否打开
        if (!this.isEnabled(annotationMetadata)) {
    
    
            return NO_IMPORTS;
        } else {
    
    
          //<2>.获取所有需要装配的bean
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

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

The method call chain is as follows:

Now let's analyze it in detail in combination with the insert image description here
combined source code:getAutoConfigurationEntry()

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 new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

step 1:

Determine whether the automatic assembly switch is turned on. Default spring.boot.enableautoconfiguration=true, can be set in application.propertiesorapplication.yml

insert image description here

Step 2:

excludeUsed to obtain and in the EnableAutoConfiguration annotation excludeName.

insert image description here

step 3

Get all configuration classes that need to be autowired, readMETA-INF/spring.factories

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

insert image description here

From the figure below, we can see that the configuration content of this file has been read by us. The role of XXXAutoConfiguration is to load components on demand.

insert image description here

Not only this dependency META-INF/spring.factoriesis read, but all META-INF/spring.factories under Spring Boot Starter will be read.

So, you can clearly see that druidthe Spring Boot Starter of the database connection pool creates the META-INF/spring.factories file.

If we want to create a Spring Boot Starter ourselves, this step is essential.

insert image description here

Step 4:

At this point, the interviewer may ask you: " spring.factoriesWith so many configurations in the system, do you have to load them all every time you start it?".

Clearly, this is unrealistic. After debugging, you will find that the value of configurations has become smaller.

insert image description here

Because, this step has gone through a round of screening, and @ConditionalOnXXXall the conditions in it are met, and the class will take effect.

@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({
    
     RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
    
    
}

Those who are interested can learn more about the conditional annotations provided by Spring Boot

  • @ConditionalOnBean: When there is a specified Bean in the container
  • @ConditionalOnMissingBean: When there is no bean specified in the container
  • @ConditionalOnSingleCandidate: When the specified Bean has only one in the container, or although there are multiple but specify the preferred Bean
  • @ConditionalOnClass: When there is a specified class in the class path
  • @ConditionalOnMissingClass: When there is no specified class in the class path
  • @ConditionalOnProperty: Whether the specified property has the specified value
  • @ConditionalOnResource: Whether the class path has the specified value
  • @ConditionalOnExpression: Based on the SpEL expression as the judgment condition
  • @ConditionalOnJava: Based on the Java version as a judgment condition
  • @ConditionalOnJndi: The difference is at the specified location under the condition that JNDI exists
  • @ConditionalOnNotWebApplication: under the condition that the current project is not a Web project
  • @ConditionalOnWebApplication: under the condition that the current project is a Web project

4. How to implement a Starter

Just talking and not practicing fake tricks, let's start a starter now to realize a custom thread pool

The first step is to create threadpool-spring-boot-startera project

insert image description here

The second step is to introduce Spring Boot related dependencies

insert image description here

The third step is to create ThreadPoolAutoConfiguration

insert image description here

The fourth step is to create the META-INF/spring.factories file under the resources package of the threadpool-spring-boot-starter project

insert image description here

Last new project importthreadpool-spring-boot-starter

insert image description here

Test passed! ! !

insert image description here

Summarize

Spring Boot @EnableAutoConfigurationrealizes automatic assembly by enabling automatic assembly through the automatic configuration class in SpringFactoriesLoaderthe final loadingMETA-INF/spring.factories

The automatic configuration class is actually @Conditionala configuration class that is loaded on demand. If you want it to take effect, you must import spring-boot-starter-xxxthe package to implement the starting dependency.

Guess you like

Origin blog.csdn.net/ldy007714/article/details/130619955