自动配置原理
SpringBoot2 引导加载自动配置类
1、@SpringBootApplication <=> @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
2、@SpringBootConfiguration:底层为 @Configuration,标识当前类为配置类
3、@ComponentScan:扫描包路径下的组件
4、@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
(1)@AutoConfigurationPackage:自动配置包,指定了默认的包规则
//往容器中导入一个Registrar组件,再利用Registrar给容器中导入一系列组件
//导入指定的一个包下的所有组件
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage
(2)@Import(AutoConfigurationImportSelector.class)
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//利用getAutoConfigurationEntry(annotationMetadata),给容器中批量导入一些组件
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
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) {
//利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
}
public final class SpringFactoriesLoader {
/*
加载META-INF/spring.factories
默认扫描当前系统中所有包的META-INF/spring.factories
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//获取资源文件
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
}
按需开启自动配置项
1、spring-boot-autoconfigure 的 jar 包
(1)SpringBoot3 以下:存在 META-INF/spring.factories 文件
(2)SpringBoot3 及以上:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
2、文件中写死 Spring Boot 启动时,给容器中加载的所有配置类
3、所有自动配置启动的时候默认全部加载,xxxAutoConfiguration 都会按照条件装配规则(@Conditional),最终会按需配置
修改默认配置
1、Spring Boot 默认会在底层配好所有的组件,但优先应用自定义配置
2、顺序:xxxAutoConfiguration -> 组件 -> xxxProperties 中取值 ----> application.properties
(1)Spring Boot 先加载所有的自动配置类 xxxxxAutoConfiguration
(2)每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值,值存放在 xxxxProperties 中,xxxProperties 和配置文件进行绑定
(3)生效的配置类给容器中装配对应组件,即容器开启对应功能
3、定制化配置
(1)方式一:自定义 @Bean 替换底层组件
(2)方式二:修改底层组件,对应配置文件的值
SpringBoot3 自动配置流程(以 Web 场景为例)
1、导入 spring-boot-starter-web:导入 Web 开发场景
(1)场景启动器导入了相关场景的所有依赖:spring-boot-starter-json、spring-boot-starter-tomcat、spring-boot-spring-webmvc
(2)每个场景启动器都引入一个 spring-boot-starter(核心场景启动器)
(3)核心场景启动器引入 spring-boot-autoconfigure
(4)spring-boot-autoconfigure 包括所有场景的所有配置
(5)只要 spring-boot-autoconfigure 包下的所有类都能生效,那么相当于 SpringBoot 官方的整合功能就生效
(6)SpringBoot 默认扫描启动类所在包及其子包,而不扫描 spring-boot-autoconfigure 下写好的所有配置类
2、主程序:@SpringBootApplication
(1)@SpringBootApplication 由三个注解组成:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
(2)SpringBoot 默认只扫描主程序所在的包及其下面的子包,扫描不到 spring-boot-autoconfigure 包中官方写好的配置类
3、@EnableAutoConfiguration:SpringBoot 开启自动配置的核心
(1)由 @Import(AutoConfigurationImportSelector.class) 提供功能:批量给容器中导入组件
(2)SpringBoot 启动默认加载所有配置类
(3)所有默认配置类是由 spring-boot-autoconfigure 下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件指定
(4)项目启动时利用 @Import 批量导入组件机制把 autoconfigure 包下的所有 xxxAutoConfiguration 类导入进来(自动配置类)
(5)按需生效:每一个自动配置类,都有条件注解 @ConditionalOnxxx,只有条件成立,才能生效
4、xxxAutoConfiguration 自动配置类
(1)给容器中使用 @Bean 放一堆组件
(2)每个自动配置类都可能有这个注解 @EnableConfigurationProperties(xxxProperties.class),用来把配置文件中配的指定前缀的属性值封装到 xxxProperties 属性类中
(3)往容器中导入的所有组件的一些核心参数,都来自于 xxxProperties 类,xxxProperties 类都是和配置文件绑定
(4)只需要改配置文件的值,核心组件的底层参数就能修改
(5)以 Tomcat 为例:@EnableConfigurationProperties(ServerProperties.class) -> @ConfigurationProperties(prefix = "server", ignoreUnknownFileds = "true"):把服务器的所有以 server 为前缀的配置,都封装到了属性类中,并忽略未知配置
5、业务
核心流程总结
1、导入相关场景 starter -> 导入 spring-boot-starter -> 导入 spring-boot-autoconfigure
2、autoconfigure 包中文件 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,指定启动时所要加载的自动配置类
3、@EnableAutoConfiguration 自动导入所有自动配置类
4、自动配置类 xxxAutoConfiguration
(1)存在条件注解 @ConditionalOnxxx,进行按需加载
(2)给容器中导入一堆组件,组件都是从 xxxProperties 类中提取属性值
5、xxxProperties 类和配置文件进行绑定
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战