如何理解SpringBoot starters的自动装配

一、什么是 SpringBoot 自动装配?

Spring Framework 早就实现了这个功能。Spring Boot 只是在其基础上,通过 SPI 的方式,做了进一步优化

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories 文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

没有 SpringBoot 情况下,如果需要引入外部依赖,需要手动依赖 jar 包。在SpringBoot 中,可以直接引入一个 starter 即可。如果想在项目中使用 json 依赖,直接在项目中引入对应的 starter 即可,引入之后,使用一些少量的注解和简单的配置就能使用引入的功能了。

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>

简单来说就是,引入依赖告诉 SpringBoot 要使用什么,SpringBoot 会通过注解和少量配置完成功能实现。

二、SpringBoot 的自动装配以及按需加载

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootDemo3Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemo3Application.class, args);
    }

}

SpringBoot 启动类上的注解 @SpringBootApplication ,可以看成是

@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类

@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制;

@ComponentScan:扫描被 @Component ( @Service, @Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean 的组合

java 复制代码
@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 这个注解是自动装配的核心注解,但是其核心功能还是使用 AutoConfigurationImportSelector 加载自动装配类实现的,在下面的第16行代码可以看到,@Import 该类。

java 复制代码
package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

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

AutoConfigurationImportSelector 的继承体系如下:

java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

}

public interface DeferredImportSelector extends ImportSelector {

}

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

AutoConfigurationImportSelector 实现了接口 ImportSelector 中的 selectImports 方法,此方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IOC 容器中

java 复制代码
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

此处的 getAutoConfigurationEntry() 方法负责加载自动配置类

java 复制代码
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        configurations = this.removeDuplicates(configurations);
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = this.getConfigurationClassFilter().filter(configurations);
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

if (!this.isEnabled(annotationMetadata)) 判断自动装配是否打开。默认 spring.boot.enableautoconfiguration=true,可在application.properties 或 application.yml 中进行配置。

AnnotationAttributes attributes = this.getAttributes(annotationMetadata); 获取 @EnableAutoConfiguration 注解中的 exclude 和 excludeName ,取消这些内容的装配。

configurations = this.removeDuplicates(configurations); 获取需要自动装配的所有配置类 ,读取 META-INF/spring.factories 文件。

此时,所有的配置文件都被读取到,可以看到 configurations 的大小为148

可以看到这个文件的配置内容都被读取到了,不光是这个依赖下的 META-INF/spring.factories 被读取到,所有 Spring Boot Starter 下的 META-INF/spring.factories 都会被读取到

并不是所有配置在 SpringBoot 启动时都会加载,

此处 configurations 的大小变成了27,这一步经历了一遍筛选,完成按需加载。

相关推荐
uzong2 小时前
技术故障复盘模版
后端
GetcharZp2 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程2 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
lifallen2 小时前
Java Stream sort算子实现:SortedOps
java·开发语言
IT毕设实战小研2 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi3 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
没有bug.的程序员3 小时前
JVM 总览与运行原理:深入Java虚拟机的核心引擎
java·jvm·python·虚拟机
一只爱撸猫的程序猿3 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋4 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
阿华的代码王国4 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端