A brief analysis of springboot automatic configuration

Springboot is essentially a spring scaffolding. In fact, it is still spring, but springboot helps us do the configurations we need to make when using spring. This article will briefly discuss how sprigboot helps us configure spring.

Reference: https://www.bilibili.com/video/BV1eD4y1w7Rp?p=2&vd_source=e1ec5e4886fca4c5f06887c81aefd01a

1. The basic idea of ​​springboot

After adding the spring-boot-starter-web dependency, we define a controller and write an interface as follows, which can be accessed in the browser. Check the console and see that tomcat is started.

@RestController
public class UserController {
    
    
    @GetMapping("/test")
    public String test(){
    
    
        return  "rrrr";
    }
}

This is actually the content of spring mvc. The process of spring mvc is as follows. When the springboot project starts, tomcat needs to be started. DispatcherServlet needs to be known in tomcat, so that tomcat can find the requested method through DispatcherServlet. At the same time, DispatcherServlet needs to know the UserController class. How to know? Just get the spring container. UserController is annotated with @RestController and is a Bean in the container.
Insert image description here
What about containers? Container initialization requires a configuration file, similar to the following. When the container starts, it needs to scan the above UserController class.

AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();//使用注解配置的容器
applicationContext.register(c);//c是一个配置类,Class类型

1.1 Starting tomcat

When starting the springboot project, you can start tomcat like this: There is a method specifically used to start tomcat, so when starting the springboot project, you can call the startTomcat method and pass in a spring container object to start tomcat.

	/**
     * 启动内嵌的tomcat,先引入pom,并进行相关的配置
     */
    public static void startTomcat(ApplicationContext context){
    
    
        //1。 进行必要的tomcat配置
        Tomcat t = new Tomcat();

        //2。输入地址想要访问到控制器,需要servlet,需要mvc的分发器(dispatcher),分发器需要容器才能找到controller
        //t.addServlet(context,"dispatcher",new DispatcherServlet(webContex));
    }

Prerequisite: There must be a dependency on tomcat.
Disadvantage: It is hard-coded. If you want to change to a web container, such as netty, it cannot be done.

1.2 Use the idea of ​​interface-oriented programming

public interface WebServer {
    
    
    public void start();
}

public class JettyWebServer implements WebServer{
    
    
    @Override
    public void start() {
    
    
        //启动jetty就需要new一些jetty的恭喜,所以需要引入jetty的依赖(此时jetty和tomcat的依赖都有)
    }
}

public class TomcatWebServer implements WebServer{
    
    
    @Override
    public void start() {
    
    
		//1.1中startTomcat的代码
    }
}

Assuming that the above class is specifically used to start the container, when starting the springboot project, you can start tomcat or jetty in the following way: the user manually adds the bean of tomcat or jetty to the container, so that the getWebServer method will be used when the springboot project starts. Automatically obtain the Bean added by the user, thereby enabling the user to customize the type of web service container.

	//springboot项目启动时执行
	WebServer webServer = getWebServer(applicationContext);
    webServer.start();

    public static WebServer getWebServer(WebApplicationContext applicationContext){
    
    
        //从容器中拿到WebServer的Bean,可能会有多个(用户自己决定)
        Map<String, WebServer> webServers = applicationContext.getBeansOfType(WebServer.class);

        if(webServers.size() == 0){
    
    

        }
        if(webServers.size() > 1){
    
    

        }
        //返回唯一的webserver对象
        return  webServers.entrySet().stream().findFirst().get().getValue();
    }

Premise: The springboot parent project needs to have dependencies on tomcat and jetty, because the above WebServer interface is defined in the springboot parent project.
Disadvantages: Users still need to write their own beans, which is obviously not what springboot does.

1.3 Choose the web container you want to use based on the pom

Define the following class on the basis of 1.2. @Conditional means that the Bean will only take effect if it meets the conditions in the class in the annotation. At this time, you only need to determine whether there are Tomcat dependencies or jetty dependencies, and you can automatically add beans to the container, eliminating the need for user-defined beans. In fact, it now has the flavor of automatic configuration.

@Configuration//这个类需要确保生效,可以使用import
public class WebServerAutomaticConfiguration {
    
    
    @Bean
    @Conditional(TomcatCondition.class)//条件满足才创建bean
    public TomcatWebServer getTomcatServer(){
    
    
        return new TomcatWebServer();
    }

    @Bean
    @Conditional(JettyCondition.class)//条件满足才创建bean
    public JettyWebServer getJettyWebServer(){
    
    
        return new JettyWebServer();
    }
}

Considering the conditions of the conditional class in the annotation, it must be judged whether a certain dependency has been introduced. How to judge whether a certain dependency has been introduced? Use the class loader to load a certain class in the dependency. If the loading is successful, it means that this dependency exists.
This is actually better, but it also has disadvantages:
Disadvantage: WebServerAutomaticConfiguration needs to be added to the container, but it is not under the package path of the configuration class, so it needs to be processed separately with Import annotations. There are many such classes for automatic configuration, so this cannot be done.

**Note:** As shown above, the springboot parent project needs to have dependencies on tomcat and jetty, because the springboot parent project needs to execute and start their code. After inheriting the springboot parent project, there will also be these two dependencies, so that in Subprojects will always have dependencies on tomcat and jetty. What should I do? It's very simple, just let the sub-project not inherit the jetty dependency of the parent project.

        <!--
                这里springboot是使用jetty依赖依赖,但是在user中,引入springboot后会有jetty和tomcat,
            这样就有问题了,所以要保证这个依赖不被user项目引用。这样user中使用jetty时需要自己重新加入依赖,同时要排除springboot中的tomcat依赖。
        -->
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>10.0.14</version>
            <optional>true</optional>
        </dependency>

2. springboot automatic configuration

2.1 Container configuration class

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

@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 {
    
    
//……………………
}

The run method is passed in DemoApplication.class. You can see from the annotations that this is a configuration class. In fact, this configuration class is used to create containers. At the same time ComponentScan, the package path for scanning is not specified, so spring will default DemoApplication.classto the scanning path (changing the configuration class in another location will not work).

2.2 Automatic configuration project

spring-boot-starterThere is a spring-boot-autoconfigureproject in the springboot parent project , which records the automatic configuration classes of many third-party projects. When springboot starts, these configuration classes will be loaded into the container to complete automatic configuration, just like the above WebServerAutomaticConfigurationclass.
Insert image description here
But there is a problem. A certain dependency, such as mybatis, spring-boot-autoconfiguredoes not have a corresponding automatic configuration class. So mybatis's automatic configuration class is in mybatis' own project. How does springboot find the configuration class at this time?

At this time, we actually need to use the famous spring.factorise file.

2.3 spring.factorise

Insert image description here

autoconfigureAs shown in the figure, there is also a project in the parent project of mybatis . In fact, there is also a spring.factorise, which is the path of the automatic configuration class.

Therefore, the function of the spring.factorise file is to identify that this is an automatic configuration project, and at the same time, it gives the path to the automatic configuration class, so that the container can load this class and complete automatic configuration like in 1.3.

Note: In fact, this file also exists in spring-boot-autoconfigurethe project.

In this way, batch scanning of automatic configuration classes is realized, and at the same time, filtering can be performed based on whether there are corresponding classes. For example, spring-boot-autoconfigurethere are automatic configuration classes for es, but they may not be used in my project. At this time, I choose whether to scan es configuration classes based on whether there are dependencies. .

Supplement: This is why we must configure the data source when we introduce the dependency of mysql.

2.4 Implementation of dpringboot

@Import({AutoConfigurationImportSelector.class})The steps described in 2.3 are all in the class annotated with @EnableAutoConfiguration.

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({
    
    AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    
    
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {
    
    };
}

The logic of loading configuration is in AutoConfigurationImportSelectorthe getAutoConfigurationEntry method

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    
    
        if (!this.isEnabled(annotationMetadata)) {
    
    
            return EMPTY_ENTRY;
        } else {
    
    
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//获取所有配置类,会扫描所有的spring.factories
            configurations = this.removeDuplicates(configurations);//去重
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);//过滤掉没有引入依赖的配置类路径
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

If there are any mistakes, please criticize and correct them!

Guess you like

Origin blog.csdn.net/baidu_40120883/article/details/129912776