Spring Boot 自动配置原理


@SpringBootApplication

  1. Spring Boot原则是“约定优于配置”,即之前已经约定(声明)好了,不需要在进行多余的配置,以此简化开发。
  2. SpringBootApplication、EnableAutoConfiguration 等关于自动配置的函数/注解/spring.factories文件 都在 jar包 spring-boot-autoconfigure 里面。
  3. 本文以 2.5.5 版本为解析源码。

在 Spring Boot 项目的入口类中有一个注解 @SpringBootApplication ,它是 Spring Boot 的核心注解,一切都由它开始。

@SpringBootApplication 源码:

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

其中有一个注解 @EnableAutoConfiguration,它管理 Spring Boot 的自动配置功能。

SpringApplication.run();

SpringBoot有一个启动类,是一个main方法(java程序的入口),里面引用了SpringApplication.run()方法,从run的源码中可以看到refreshContext(context);方法,这个方法可以进行容器刷新,读取配置文件,扫描注解。源码如下:
在这里插入图片描述

@EnableAutoConfiguration

源码解析

@EnableAutoConfiguration源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    // ...
}

@EnableAutoConfiguration 注解通过@Import({AutoConfigurationImportSelector.class})来完成导入配置的功能,类 AutoConfigurationImportSelector 通过 selectImports()等一系列函数,最后调用 SpringFactoriesLoader.loadFactoryNames 方法加载 META-INF/spring.factories 文件,并进行自动配置所需要的 第三方依赖/配置/包。

AutoConfigurationImportSelector 的部分源码如下:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
//	
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	// 扫描具有META-INF/spring.factories文件的jar包
	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);
}
//
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
			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 部分源码:

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());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();
        try {
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");
            // ...
    	}
	}
}

spring.factories自动配置所需要的声明都在# Auto Configure下面,spring.factories是一个key=value,value,… 形式的文件。
spring.factories在jar包位置
任何一个springboot应用,都会引入spring-boot-autoconfigure,而spring.factories文件就在该包下面。该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration
Auto Configure
EnableAutoConfiguration 配置了多个类,这些都是Spring Boot中的自动配置相关类,在启动过程中会解析对应类配置信息。
每个Configuation类都定义了相关bean的实例化配置,都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来。
如果我们自定义了一个starter的话,也要在该starter的jar包中提供 spring.factories文件,并且为其配置org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类。
所有框架的自动配置流程基本都是一样的,判断是否引入框架,获取配置参数,根据配置参数初始化框架相应组件。

简单理解
  1. Spring Boot在启动时,会根据 jar包 中的 (FACTORIES_RESOURCE_LOCATION=)META-INF/spring.factories 进行声明,并将这些依赖引入本项目,通过@EnableAutoConfiguration开启使用。
  2. spring-boot-autoconfigure-2.5.5.jar/META-INF/spring.factories( “# Auto Configure” 中 找到相应的三方依赖(暂时131个),其第三方包都在 spring-boot-autoconfigure-2.5.5.jar/org.springframework.boot.autoconfigure 目录下)。
  3. spring-boot-autoconfigure-2.5.5.jar 中包含了 J2EE整合体系中 需要的依赖。
@AutoConfigurationPackage

@AutoConfigurationPackage 作用是 引入自己写的包,
可以找到我们用了@SpringBootApplication 的所在类的包名,作用:会将我们自己写的类所在的包及其所有的子包 全部纳入spring容器(不需要配置包扫描器等)(com.sb.xxx.xxxx)。

举个荔枝

HttpEncodingAutoConfiguration

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {//...}

自动装配的类(例如HttpEncodingAutoConfiguration)中:包含了

  1. @Configuration:表示此类是一个配置类;将此类纳入springIoC容器中;

  2. @EnableConfigurationProperties(ServerProperties.class) :通过ServerProperties将编码默认为 UTF-8 等信息;

  3. @ConditionalOnWebApplication、@ConditionalOnClass、@ConditionalOnProperty 等:

    3.1 每一个 XxxAutoConfiguration 都有很多条件 @ConditionalOnXxx,当这些条件都满足时,测该配置 自动装配生效。但是我们可以在 application.properties 中修改:

     3.1.1 @ConditionalOnProperty 的prefix的 内容.属性名=值
     	例:@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
     		(eg: server.servlet.encoding.charset=GBK)。
     3.1.2 @EnableConfigurationProperties 对应的XxxProperties(eg:@ConfigurationProperties(prefix = "spring.data.mongodb"))类中的prefix的内容.属性名
    

    3.2 当属性满足所有要求(ConditionalOnXXX)时,此条件成立:要求:如果没有配置 server.servlet.encoding.enabled=xxx(GBK等),则成立。

  4. 如何知道springboot开启了哪些自动装配、禁止了哪些自动装配:application.properties中加 debug=true 即可。

    日志中  Positive matches 表示 spring boot 自动开启了的转配内容;
    Negative matches 表示 spring boot 没有自动开启的装配内容
    
  5. springboot就是用过 XxxAutoConfiguration 实现自动装配,如果想要修改默认值,通过 XxxProperties 中的 prefix 进行修改。(web.xml spring mvc.xml)

甩图看看 自动配置流程

在这里插入图片描述

面试简答

SpringBoot启动时会通过注解@EnableAutoConfiguration找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性(如:server.port),而XxxProperties类是通过注解@ConfigurationProperties与全局配置文件中对应的属性进行绑定的。


总:SpringBoot启动类是一个main方法,里面调用的是SpringApplication的静态方法,run方法,在run方法里面能一直点进去另一个run方法,直到一个run方法里面有一个refreshContext(context),他能够刷新容器,他读你的配置文件,扫你的注解,只要你一刷新容器,他就会扫,于是你才可以在SpringBootApplication注解里往深的点。
SpringBootApplication里面有一个重要的注解:@EnableAutoConfiguration,他开启的SpringBoot的自动配置,他在里面引入了一个类,叫做AutoConfigurationImportSelector,这个类会有一个核心方法selectImports(),帮助我们从类路径下的META-INF的spring.factories下加载一些东西,他会跟着很多的配置文件,配置类,他会加载所有的配置类,加载到容器里面,他里面有很多的条件注解,会根据你引入的jar包,注入的bean,把相应的bean自动的注入到我们的容器里面,于是就实现了自动装配。


简:SpringBoot在启动的时候,会调用run方法,run方法会刷新容器,刷新容器的时候他会从类路径下找到spring.factories文件,在这个文件里面记录了好多自动配置类,我们会在启动的时候把这个配置类加载到容器里面,这些配置类里面有好多的条件注解,他会根据我们有没有引入相应的jar包,有没有注入一些bean来自动的给我们的容器注入我们需要的bean,于是就实现了自动装配。


相关阅读:

小白学SpringBoot

~未完待续,如果有帮到您,点个赞哩!谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值