SpringBoot--自动配置原理分析

Posted dxj1016

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot--自动配置原理分析相关的知识,希望对你有一定的参考价值。

我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml文件探究起;

1、pom.xml

父依赖
其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!
在这里插入图片描述
点进去,发现还有一个父依赖和一些资源配置,比如yml
在这里插入图片描述
在这里插入图片描述
spring-boot-dependencies点进去可以看到有很多版本仓库
在这里插入图片描述

  • spring-boot-dependencies:核心依赖在父工程中
  • 我们在写或者引入一些springboot依赖的时候,不需要指定版本,就因为有这些版本仓库

这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

2、启动器spring-boot-starter

默认有一个这样的依赖,如果没有这个那么springboot下面的就崩了。

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

在这里插入图片描述
官方文档
在这里插入图片描述

  1. springboot-boot-starter-xxx:就是spring-boot的场景启动器
  2. spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;
  3. SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;

3、主程序

分析完了 pom.xml 来看看这个启动类

//@SpringBootApplication 来标注这一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {

   public static void main(String[] args) {
     //将springboot应用启动
      SpringApplication.run(SpringbootApplication.class, args);
   }

}

3.1、run方法详细分析

不简单的方法
我最初以为就是运行了一个main方法,没想到却开启了一个服务;

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

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;在这里插入图片描述
查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

run方法流程分析
在这里插入图片描述
对照上面的图查看相对应的源码
在这里插入图片描述
springboot启动过程:

springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,而它是通过启动类的SpringBootApplication注解进行开始解析的,他会根据EnableAutoConfiguration开启自动化配置,里面有个核心方法ImportSelect选择性的导入,根据loadFanctoryNames根据classpash路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解,先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器

4、启动类的注解分析

4.1、@SpringBootApplication

在这里插入图片描述

  • 作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
  • 进入这个注解:可以看到上面还有很多其他注解!
    在这里插入图片描述
    进去之后有这些注解
    在这里插入图片描述

4.1.1、@ComponentScan

  • 扫描当前主启动类同级的包
  • 这个注解在Spring中很重要 ,它对应XML配置中的元素。
  • 作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

4.1.2、@SpringBootConfiguration

  • 作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
  • 我们继续进去这个注解查看
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
//...
}
  • 可以看到进去之后还有有一个@Configuration注解

4.1.2.1、@Configuration

  • 这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
  • 这个注解点进去还有@Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
//
}
  • @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
  • 我们回到 SpringBootApplication 注解中继续看。

4.1.3、@EnableAutoConfiguration

  • @EnableAutoConfiguration :开启自动配置功能,自动导入包
  • 以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;
  • @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
  • 点进注解接续查看:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//
}
  • @AutoConfigurationPackage注解
  • @Import(AutoConfigurationImportSelector.class)注解

4.1.3.1、@AutoConfigurationPackage

  • @AutoConfigurationPackage :自动配置包
  • 点进去里面查看
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
//
}
  • @Import(AutoConfigurationPackages.Registrar.class):自动注册包,扫描到包之后就进行注册
  • @import:Spring底层注解@import , 给容器中导入一个组件
  • Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;
  • 这个分析完了,退到上一步,继续看

4.1.3.2、@Import({AutoConfigurationImportSelector.class})

  • @Import({AutoConfigurationImportSelector.class}):自动导入包的核心,给容器导入组件 ;
  • AutoConfigurationImportSelector:自动配置导入选择器,那么它会导入哪些组件的择器呢?我们点击去这个类看源码:
    • 方法getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)获得自动配置的实体
      • 方法里面调用方法:getCandidateConfigurations(annotationMetadata, attributes);:获取候选的配置
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		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 = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
  • 点击getCandidateConfigurations进来方法getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
    • 这个方法又调用了SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类有个loadFactoryNames() 方法,这个方法有一个参数getSpringFactoriesLoaderFactoryClass()
// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    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;
}
  • 点进去getSpringFactoriesLoaderFactoryClass()有如下方法。
	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;//标注了EnableAutoConfiguration注解的类
	}

  • 进入SpringFactoriesLoaderloadFactoryNames()方法:获取所有的加载配置

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //这里它又调用了 loadSpringFactories 方法
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
  1. 继续点击查看loadSpringFactories类。可以获取项目资源和系统资源。
    在这里插入图片描述
  2. 点击资源那里的参数FACTORIES_RESOURCE_LOCATION进去可以获取配置资源路径,知道从这里获取配置。
    在这里插入图片描述
  3. 继续看下面的代码有个while循环,从这些资源中遍历所有的nextElement(自动配置),遍历完成之后,封装为Properties供我们使用
    在这里插入图片描述
  4. 发现一个多次出现的文件:spring.factories,全局搜索它。所有的自动配置类都在这里
    在这里插入图片描述
  5. 思考:spring.factories里面有很多自动配置为什么有的没有生效,需要导入对应的start才能有作用!
  6. 有个核心注解@ConditionalOnxxx:如果这里面的条件都满足,才会生效。
    在这里插入图片描述

结论:springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功了

  1. springboot在启动的时候,从路径下/META-INF/spring.factories获取指定的值
  2. 将这些自动装配的类导入容器,自动配置就会生效,帮我们进行自动配置
    在这里插入图片描述
  3. 以前我们需要手动配置的东西,现在springboot帮我们做了
  4. 整合javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下
  5. 他会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器
    在这里插入图片描述
  6. 容器中也会存在非常多的xxxAutoConfiguration(@Bean)的文件,就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,JavaConfig
  7. 有了自动配置类,免去了我们手动编写配置文件的工作

4.2、spring.factories

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
在这里插入图片描述
WebMvcAutoConfiguration
我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration
在这里插入图片描述
可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
在这里插入图片描述
结论:

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 ,
    并配置好这些组件 ;
  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

现在大家应该大概的了解了下,SpringBoot的运行原理,后面我们还会深化一次!

springboot–配置文件

以上是关于SpringBoot--自动配置原理分析的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot自动配置原理分析

springboot自动配置原理分析

SpringBoot自动配置原理分析

SpringBoot自动配置原理(源码分析)

SpringBoot 高级 原理分析 -- SpringBoot 自动配置:Condition

SpringBoot05:自动配置原理