Spring Boot 自动配置原理
@SpringBootApplication
- Spring Boot原则是“约定优于配置”,即之前已经约定(声明)好了,不需要在进行多余的配置,以此简化开发。
- SpringBootApplication、EnableAutoConfiguration 等关于自动配置的函数/注解/spring.factories文件 都在 jar包
spring-boot-autoconfigure
里面。 - 本文以 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,… 形式的文件。
任何一个springboot应用,都会引入spring-boot-autoconfigure,而spring.factories文件就在该包下面。该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration
EnableAutoConfiguration 配置了多个类,这些都是Spring Boot中的自动配置相关类,在启动过程中会解析对应类配置信息。
每个Configuation类都定义了相关bean的实例化配置,都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来。
如果我们自定义了一个starter的话,也要在该starter的jar包中提供 spring.factories文件,并且为其配置org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类。
所有框架的自动配置流程基本都是一样的,判断是否引入框架,获取配置参数,根据配置参数初始化框架相应组件。
简单理解
- Spring Boot在启动时,会根据 jar包 中的
(FACTORIES_RESOURCE_LOCATION=)META-INF/spring.factories
进行声明,并将这些依赖引入本项目,通过@EnableAutoConfiguration开启使用。 - 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 目录下)。
- spring-boot-autoconfigure-2.5.5.jar 中包含了 J2EE整合体系中 需要的依赖。
@AutoConfigurationPackage
@AutoConfigurationPackage 作用是 引入自己写的包,
可以找到我们用了@SpringBootApplication 的所在类的包名,作用:会将我们自己写的类所在的包及其所有的子包 全部纳入spring容器(不需要配置包扫描器等)(com.sb.xxx.xxxx)。
举个荔枝
@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)中:包含了
-
@Configuration:表示此类是一个配置类;将此类纳入springIoC容器中;
-
@EnableConfigurationProperties(ServerProperties.class) :通过ServerProperties将编码默认为 UTF-8 等信息;
-
@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等),则成立。
-
如何知道springboot开启了哪些自动装配、禁止了哪些自动装配:application.properties中加 debug=true 即可。
日志中 Positive matches 表示 spring boot 自动开启了的转配内容; Negative matches 表示 spring boot 没有自动开启的装配内容
-
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,于是就实现了自动装配。
相关阅读:
~未完待续,如果有帮到您,点个赞哩!谢谢!