在阅读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类名。
————————————————
版权声明:本文为CSDN博主「SkyeBeFreeman」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SkyeBeFreeman/article/details/96291283

为什么我要写spring.factories文件?的更多相关文章

  1. 关于META-INF下的spring.factories文件

    spring.factories 文件是springboot提供的一种实例化bean方式 org.springframework.boot.autoconfigure.EnableAutoConfig ...

  2. 建立META-INF/spring.factories文件的意义何在

    平常我们如何将Bean注入到容器当中 @Configuration @EnableConfigurationProperties(HelloProperties.class) public class ...

  3. Spring.factories扩展机制

    和Java SPI的扩展机制类似,Spring Boot采用了spring.factories的扩展机制,在很多spring的starter 包中都可以找到,通过在 META-INF/spring.f ...

  4. spring.factories

    在Spring Boot中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的. Java SPI机制SPI的全名为Service P ...

  5. [SpringBoot] 通过spring.factory文件来加载第三方的bean

        在springboot的开发过程中,我们经常需要加载一些bean,如果bean使我们自己写的类,那很好办,加个@Component注解就搞定了,然后过程启动会扫描启动类所在的包及其子包,如果我 ...

  6. 教你写Spring组件

    前言 原文地址:https://www.cnblogs.com/qnlcy/p/15905682.html 一.宗旨 在如日中天的 Spring 架构体系下,不管是什么样的组件,不管它采用的接入方式如 ...

  7. 注意:Spring Boot 2.7开始spring.factories不推荐使用了,接下来这么玩...

    如果你是Spring Boot用户的话,一定有这样的开发体验,当我们要引入某个功能的时候,只需要在maven或gradle的配置中直接引入对应的Starter,马上就可以使用了,而不需要像传统Spri ...

  8. java SPI & spring factories

    SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.SPI是一种动态替换发现的机制, 比如有个接口,想运行时动态的给它添加实现,你只需要添加 ...

  9. spring.factories配置文件的工厂模式

    在springboot的各个依赖包下,我们经常看到META-INF/spring.factories这个文件.spring.factories文件的内容基本上都是这样的格式: # Initialize ...

  10. SpringBoot之spring.factories

    组件提供者如何编写出仅需系统开发者进行包引入就可以对spring进行bean注入等操作?   其实在spring库中有提供自动化配置的库spring-boot-autoconfigure,我们只需要引 ...

随机推荐

  1. vue table 里面 slot 的模板复用 slot-scope template v-for

    vue table 里面 slot 的模板复用 slot-scope template v-for 需求 经常在table里面要有自定义列,但是会有相同的自定义列,这个时候又不想写很多一样的templ ...

  2. gap 单词学习 对标 open

    为什么gap 和 open 联系记忆呢? gap是从行为动作中来 open 中 op 就是 up,是从 单词字母的角度来 但是 本意 这两个单词都差不多 gap gap : 来自PIE*ghai,打呵 ...

  3. 【大语言模型基础】GPT(Generative Pre-training )生成式无监督预训练模型原理

    GPT,GPT-2,GPT-3 论文精读[论文精读]_哔哩哔哩_bilibili   ELMo:将上下文当作特征,但是无监督的语料和我们真实的语料还是有区别的,不一定符合我们特定的任务,是一种双向的特 ...

  4. springboot+vue3+nuxt3+ts+minio开发的dsblog3.0前后端博客

    springboot+vue3+nuxt3+ts+minio开发的dsblog3.0前后端博客 转载自:www.javaman.cn 一.技术栈 本博客系统采用了先进且成熟的技术栈,包括Spring ...

  5. 什么叫运行时的Java程序?

    Java程序的运行包含编写.编译和运行三个主要步骤. 1.在编写阶段: 开发人员在Java开发环境中输入程序代码,形成后缀名为.java的Java源文件. 2.在编译阶段: 使用Java编译器对源文件 ...

  6. MapStructPlus 1.4.0 发布,体积更轻量!性能更强!

    MapStruct Plus 是 MapStruct 的增强工具,在 Mapstruct 的基础上,实现了自动生成 Mapper 接口的功能,并强化了部分功能,使 Java 类型转换更加便捷.优雅. ...

  7. C++ Concurrency in Action 读书笔记三:并发操作的同步

    Chapter 4 并发操作的同步·Synchronizing concurrent operations

  8. 基于VB6的磁性移动窗体 - 开源研究系列文章

    这次继续整理代码.这个磁性窗体是以前大学的时候开发的,当时模仿的Winamp的效果进行的编程.当时的时候有Windows API函数能够进行处理,但是XP的年代,那个API只是移动的虚框,而不是移动窗 ...

  9. LOTO示波器选型指南

    LOTO示波器选型指南 LOTO示波器属于虚拟示波器,产品主要基于USB接口的,所以使用LOTO示波器产品需要配备一台Windows电脑或者Android(安卓)智能手机/平板. 针对一些特殊应用的工 ...

  10. docker-compose安装mysql8+踩坑版

    一.拉取MySQL镜像 我这里使用的是MySQL8.0.18,可以自行选择需要的版本. docker pull mysql:8.0.18 二.创建挂载目录 mkdir -p /home/docker/ ...