如何理解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,这一步经历了一遍筛选,完成按需加载。

相关推荐
懂得节能嘛.5 分钟前
【SpringAI实战】实现仿DeepSeek页面对话机器人(支持多模态上传)
java·spring
张乔2410 分钟前
mybatisX的自定义模板生成
java·ide·intellij-idea
笨蛋不要掉眼泪19 分钟前
Java测试题(上)
java·开发语言
ahauedu39 分钟前
用Java 代码实现一个简单的负载均衡逻辑
java·python·负载均衡
不过普通话一乙不改名44 分钟前
第一章:Go语言基础入门之函数
开发语言·后端·golang
Java初学者小白1 小时前
秋招Day18 - MyBatis - 基础
java·数据库·mybatis
大白玉米1 小时前
TVBOXOS6.0双端APP二开源码完整版全开源源码重构版
java·重构·php
苹果醋31 小时前
iview中实现点击表格单元格完成编辑和查看(span和input切换)
运维·vue.js·spring boot·nginx·课程设计
武昌库里写JAVA1 小时前
iView Table组件二次封装
vue.js·spring boot·毕业设计·layui·课程设计
Dcs1 小时前
Cisco爆出重大漏洞!无需登录即可获取Root权限,攻击者已在野利用!
java