为什么我要写spring.factories文件?

本文解析了spring-boot中自动配置的原理,详细介绍了@SpringBootApplication、@EnableAutoConfiguration等注解的作用,以及spring.factories文件在自动配置中的作用。

本文涉及spring-boot版本为2.1.6.RELEASE

在阅读spring-boot相关源码时,常常见到spring.factories文件,里面写了自动配置(AutoConfiguration)相关的类名,因此产生了一个疑问:“明明自动配置的类已经打上了@Configuration的注解,为什么还要写spring.factories文件?”

这个话题需要从@SpringBootApplication注解开始说起。

查看@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@ComponentScan两个注解。@ComponentScan注解的作用是扫描@SpringBootApplication所在的Application类(即spring-boot项目的入口类)所在的包(basepackage)下所有的@component注解(或拓展了@component的注解)标记的bean,并注册到spring容器中。

看到这里也许会有个疑问,在spring-boot项目中pom文件里面添加的依赖中的bean(spring-boot项目外的bean)是如何注册到spring-boot项目的spring容器中的呢?

这就需要讨论@EnableAutoConfiguration的作用。查看@EnableAutoConfiguration源码,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	Class<?>[] exclude() default {};
	String[] excludeName() default {};
}

我们可以看到比较关键的代码是@Import(AutoConfigurationImportSelector.class),而AutoConfigurationImportSelector.class做了什么呢?通过其源码可以看出关键的部分为,

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
			.loadMetadata(this.beanClassLoader);
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
			annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

其中,getAutoConfigurationEntry方法获取了spring-boot项目中需要自动配置的项(bean),查看其源码发现,

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
		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 = filter(configurations, autoConfigurationMetadata);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

其中最重要的部分为getCandidateConfigurations方法,它获取了所有可能参与到项目的候选配置bean,与之对应的,getExclusions获取了所有不需要加载的配置bean。进一步查看getCandidateConfigurations方法的源码,

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;
}

这个方法的具体实现为,读取spring-boot项目的classpath下META-INF/spring.factories的内容,这个文件常常以K/V的形式存储数据,例如:

# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.HelloAutoConfiguration,\
……

getCandidateConfigurations方法获取需要自动配置的类,除去上面讲到的需要排除(exclude)的配置类,其他类将会注册到spring-boot项目的spring容器中。

看到这里,想必已经了解@EnableAutoConfiguration注解的工作原理,回到最初的话题,“为什么要写spring.factories文件?”

结合前面提出的疑问——“在spring-boot项目中pom文件里面添加的依赖中的bean是如何注册到spring-boot项目的spring容器中的呢?”,不难得出spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器的结论。由于@ComponentScan注解只能扫描spring-boot项目包内的bean并注册到spring容器中,因此需要@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名。

Spring Boot 2.7+ 中,官方开始 **逐步弃用 `spring.factories`** 机制,主要原因包括: --- ## ✅ 1. **性能问题** ### 问题: `spring.factories` 是一个全局的键值对配置文件Spring Boot 在启动时会加载所有 JAR 包中的 `spring.factories` 文件,并解析其中所有的键值对。 这种方式会导致: - **启动速度变慢**,尤其是在项目依赖较多第三方库时。 - **资源浪费**:加载了大量可能用不到的自动配置类。 ### 解决方案: 使用新的 `AutoConfiguration.imports` 机制,只加载真正需要的自动配置类,提升启动效率。 --- ## ✅ 2. **模块化与可维护性差** ### 问题: `spring.factories` 是一个通用的工厂配置文件,不仅用于自动配置,还用于其他功能(如 `ApplicationContextInitializer`、`ApplicationListener` 等),导致配置项混杂,难以维护。 例如: ```properties org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.autoconfigure.MyAutoConfiguration org.springframework.context.ApplicationListener=\ com.example.event.MyApplicationListener ``` 所有配置都在一个文件中,缺乏结构化。 ### 解决方案: 引入 `AutoConfiguration.imports` 文件专门用于自动配置类声明,实现职责分离,提高可维护性。 --- ## ✅ 3. **与 Spring 的原生模块化机制不兼容** Spring Framework 从 6.0 开始更加注重模块化和条件化装配,而 `spring.factories` 的机制并不符合这种趋势。 新的 `AutoConfiguration.imports` 支持: - 更细粒度的控制 - 条件化装配(`@ConditionalOnClass`、`@ConditionalOnMissingBean` 等) --- ## ✅ 4. **构建工具和 IDE 支持更好** 新机制更利于构建工具(如 Gradle、Maven)和 IDE(如 IntelliJ IDEA、Spring Tool Suite)进行索引和分析,提升开发体验。 --- ## ✅ 5. **为 Spring Boot 3.0 做准备** Spring Boot 3.0 要求使用 Java 17+ 和 Jakarta EE 9+,同时完全移除了对 `spring.factories` 的支持。因此,2.7 版本是一个过渡版本,用于帮助开发者迁移到新的机制。 --- ## ✅ 总结对比 | 特性 | spring.factories | AutoConfiguration.imports | |------|------------------|----------------------------| | 出现版本 | Spring Boot 1.x - 2.6 | Spring Boot 2.7+ | | 用途 | 多用途配置(自动装配、监听器等) | 专用于自动装配 | | 性能 | 较差,加载所有配置 | 更好,按需加载 | | 可维护性 | 差,配置混杂 | 好,职责单一 | | 模块化支持 | 不足 | 支持良好 | | 是否被弃用 | 是(2.7+) | 推荐使用 | --- ## ✅ 示例:迁移 `spring.factories` 到 `AutoConfiguration.imports` ### 原始 spring.factories 内容: ```properties org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.autoconfigure.MyAutoConfiguration ``` ### 迁移到 AutoConfiguration.imports: 创建文件: ``` src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ``` 内容为: ``` com.example.autoconfigure.MyAutoConfiguration ``` --- ## ✅ 如何验证是否生效? ```java @Autowired private MyService myService; @Test void contextLoads() { assertNotNull(myService); } ``` 如果测试通过,说明自动配置类已被正确加载。 --- ## ✅ 工具推荐 可以使用官方提供的 `spring-boot-migrator` 插件帮助自动迁移: ```xml <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-migrator</artifactId> <version>2.7.0</version> </plugin> ``` --- ###
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值