SpringBoot的自动装配原理

SpringBoot自动装配原理

  1. pom.xml
  • spring-boot-dependencies核心依赖在父工程中
  • 在引入springboot依赖时,不需要写版本,因为父工程中有版本仓库。
  1. 启动器 spring-boot-starter-xxx
  • 例如,spring-boot-starter-web,就会帮我们导入web环境所有的依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 主程序SpringbootApplication .java
package com.arlin.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

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

}

表面理解:

  • 注解@SpringBootApplication:标注这个类是一个springboot的应用
  • SpringApplication.run(SpringbootApplication.class, args);//将springboot应用启动

深层次(源码分析):

自动装配原理分析图

img

img

  • 注解@SpringBootApplication是一个复合 Annotation

  • 核心的两个注解:@SpringBootConfiguration和@EnableAutoConfiguration

image-20210306202151496

1. @SpringBootConfiguration:springboot的配置

  • @SpringBootConfiguration注解下还包含:@Configuration:表示一个普通的 JavaConfig 形式的 IoC 容器配置类
  • @Configuration注解下包含:@Component:表示该配置类也是容器中的一个组件

2. @EnableAutoConfiguration :开启自动配置功能

  • 借助 @Import 的帮助,将所有符合自动配置条件的 bean 定义加载到 IoC 容器

  • AutoConfigurationImportSelector:导入哪些组件的选择器。将所有需要导入的组件以全类名的方式返回,这些组件会被添加其中

image-20210306202418064

① 在AutoConfigurationImportSelector类中,实现了 DeferredImportSelector 接口,而 DeferredImportSelector 是继承 ImportSelector 类的。在 ImportSelector 中有个非常重要的方法:selectImports:批量将类注入到IOC容器

// 1. 批量将类注入到IOC容器
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

② selectImports方法调用getAutoConfigurationEntry方法

// 2. selectImports方法调用getAutoConfigurationEntry方法
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);
        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);
    }
}

image-20210306204709383

③ getAutoConfigurationEntry方法调用getCandidateConfigurations方法:获取候选配置

// 3. 获取候选配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

④ 调用SpringFactoriesLoader类中的loadFactoryNames方法,之后进入loadSpringFactories方法

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

可以看到在loadSpringFactories方法中调用如下路径:META-INF/spring.factories,从中获取EnableAutoConfiguration指定的值。

image-20210306210013823

META-INF/spring.factories配置文件中包含所有自动配置类:

image-20210306213356600

SpringBoot不需要写配置文件的原因是:SpringBoot所有配置都是在启动的时候进行扫描并加载,SpringBoot的所有自动配置类都在Spring.factories里面,但是不一定会生效,生效前要判断条件是否成立,只要导入了对应的start,就有对应的启动器,有了启动器就能帮我们进行自动配置类

每一个xxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:

  • @ConditionalOnBean:当容器里有指定的bean的条件下。

  • @ConditionalOnMissingBean:当容器里不存在指定bean的条件下。

  • @ConditionalOnClass:当类路径下有指定类的条件下。

  • @ConditionalOnMissingClass:当类路径下不存在指定类的条件下。

  • @ConditionalOnProperty:指定的属性是否有指定的值
    比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。

3. @ComponentScan

@ComponentScan的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些 bean 定义加载到容器中。


结论

1. SpringBoot的自动装配原理

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,通过@Bean导入到Spring容器中,以Properties结尾命名的类是和配置文件进行绑定的。它能通过这些以Properties结尾命名的类中取得在全局配置文件中配置的属性,我们可以通过修改配置文件对应的属性来修改自动配置的默认值,来完成自定义配置。

2. SpringbootApplication类的作用

  • 1.推断应用的类型是普通的项目还是Web项目
  • 2.查找并加载所有可用初始化器,设置到initializers属性中
  • 3.找出所有的应用程序监听器,设置到listeners属性中
  • 4.推断并设置main方法的定义类,找到运行的主类
原文地址:https://www.cnblogs.com/arlenlin/p/14492418.html