SpringBoot自动装配原理

SpringBoot的自动装配是其核心特性之一,他简化了Spring应用的配置流程,实现了"约定大于配置"的理念。其核心原理可以概括为:通过特定的注解和配置文件,自动识别并加载符合条件的Bean到Spring容器中。

1.核心注解:@SpringBootApplication

自动装配的入口是主类上的@SpringBootApplication注解,它是一个符合注解,包含三个关键注解:

  • @SpringBootConfiguration:标记当前类为配置类(本质上是@Configuration的派生注解)
  • @ComponentScan:扫描当前包及其子包下的@Component,@Service等注解的类,注册为Bean。
  • @EnableAutoConfiguration:开启自动配置的核心注解,也是自动装配的关键。

2.@EnableAutoConfiguration的作用

当我们点进去这个核心注解的时候:

EnableAutoConfiguration 注解实现自动装配机制的时候,主要依赖于这个**@Import({AutoConfigurationImportSelector.class})** ,通过import的导入到AutoConfigurationImportSelector类,该类负责筛选并导入需要自动装配的类 。其中这个类的话实现自动装配依赖于**selectImports()**这个方法。

这个方法通过在内部调用getAutoConfigurationEntry方法来实现自动装配。

getCandidateConfigurations()这个方法会告诉要想将没有spingbootapplication注解启动的类,但是想让spring帮助我们进行注册Bean需要将类的全限定名加到某个文件中。

getCandidateConfigurations()这个方法具体实现:

java 复制代码
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates();
        Assert.notEmpty(configurations, "No auto configuration classes found 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;
    }

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

也就是将需要注册的类全限定名写入到这个文件中。

举一个例子:

我们在另外一个工程里面封装了redis的一些方法以及redis的相关配置。希望能够在外部中引用RedisService这个对象。但是这个工程中并没有springbootapplication启动注解。所以将这个类的全限定名写入到以上这个文件中。

有两点需要进行注意:

一是一般这个文件创建在resources这个目录下,META-INF和spring这是两个目录,先创建META-INF目录再创建spring目录。同时在创建的时候需要打开一个东西。

把compact给取消掉。是因为在勾选上的话,创建的话不容易看出这是一个二级目录,我们想要的效果就是达到以下图片的效果。

3.详细解读getAutoConfigurationEntry()

java 复制代码
if (!this.isEnabled(annotationMetadata)) {
    return EMPTY_ENTRY;
}

首先判断自动配置功能是否开启。

isEnabled()方法会检查属性@EnableAutofiguration注解的enabled属性(默认为true),也可能结合其他全局开关。如果返回false的,表示关闭自动配置,直接返回空结果。

java 复制代码
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

从注解元数据AnnotationMetadata中提取@EnableAutoConfiguration的属性信息。

@EnableAutoConfiguration有exclude,excludeName等属性(用于指定需要排除的自动配置类),getAttributes()会将这些属性解析为AnnotationAttribute对象,方便后续处理。

java 复制代码
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

获取所有候选自动配置类的全限定名

getCandidateConfigurations()方法会读取类路径下 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中,以org.springframework.boot.autoconfigure.EnableAutoConfiguration为key的所有值(这些值就是Spring Boot预定义的自动配置类,如WebMvcAutConfiguration等。

java 复制代码
configurations = this.removeDuplicates(configurations);

对候选配置类去重,避免同一个配置类被多次加载。

如果多个 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件定义了同一个自动配置类,这里确保最终只保留一个。

java 复制代码
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);

收集所有需要排除的自动配置类

@EnableAutoConfiguration注解的exclude属性(直接指定类)或者是excludeName属性指定类名。还可以通过配置文件application.yaml中通过spring.autoconfigure.exclude配置的类。

java 复制代码
this.checkExcludedClasses(configurations, exclusions);

检查需要排除的类是否确实存在于候选配置类中。

如果用户排除了一个不存在的自动配置类(如拼写错误),这里会抛出异常(illegalArgumentException),提前暴露配置错误。

java 复制代码
configurations.removeAll(exclusions);

将需要排除的类从候选配置类列表中彻底移除。

被排除的类不会被Spring容器加载,实现了"自定义排除不需要的自动配置"的功能。

java 复制代码
configurations = this.getConfigurationClassFilter().filter(configurations);

通过条件过滤器筛选出符合当前环境的配置类。

过滤器会检查每个候选配置类上的@Conditional系类注解(如@ConditionalOnClass,@ConditionalOnMissingBean等),只有满足所有条件的配置类才会被保留。

java 复制代码
this.fireAutoConfigurationImportEvents(configurations, exclusions);

触发自动配置导入事件,通知相关监听器(AutoConfigurationImportListenter)。

允许外部自动配置进行扩展(如日志记录,额外处理等)。

java 复制代码
return new AutoConfigurationEntry(configurations, exclusions);

将筛选后的有效配置类和排除类封装为AutoConfigurationEntry对象并返回。

Spring 容器会根据该结果,将configurations中的类作为类加载,从而完成自动装配。

总结:

1.先检查开关,确保自动配置启用

2.加载预定义的候选配置类

3.处理去重和排除,移除不需要的类

4.通过@Conditional注解筛选符合当前环境的类

5.发布事件并返回结果

最终,筛选后的配置类会被Spring容器加载,其内部定义的@Bean方法会生成相应的Bean,实现自动配置的效果。

相关推荐
_码农12138几秒前
java web 未完成项目,本来想做个超市管理系统,前端技术还没学。前端是个简单的html。后端接口比较完善。
java·前端·html
麦兜*31 分钟前
Spring Boot 与 Ollama 集成部署私有LLM服务 的完整避坑指南,涵盖 环境配置、模型管理、性能优化 和 安全加固
java·spring boot·后端·安全·spring cloud·性能优化
leo__52034 分钟前
Java的NIO体系详解
java·python·nio
烟沙九洲34 分钟前
服务之间远程Feign调用,出现参数丢失
java·spring boot
Yang-Never38 分钟前
Kotlin协程 ->launch构建协程以及调度源码详解
android·java·开发语言·kotlin·android studio
极客BIM工作室41 分钟前
C++返回值优化(RVO):高效返回对象的艺术
java·开发语言·c++
用户849137175471642 分钟前
JustAuth实战系列(第1期):项目概览与价值分析
java·架构·开源
埃泽漫笔1 小时前
BeanFactory 和 ApplicationContext 的区别?
spring
自由的疯1 小时前
Java 17 新特性之 instanceof 运算符
java·后端·架构
自由的疯1 小时前
Java 17 新特性之 Switch 表达式改进
java·后端·架构