浅析Spring boot自动装配原理

本文深入剖析SpringBoot框架如何通过约定优于配置的理念简化应用搭建,重点解读@SpringBootApplication、@EnableAutoConfiguration等注解的工作原理,以及SpringFactoriesLoader和条件过滤在自动装配过程中的角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是 spring boot

springboot 框架是为了能够帮助使用 spring 框架的开发者快速高效的构建一个基于 spirng 框架以及 spring 生态体系的应用解决方案。它是对“约定优于配置”这个理念下的一个最佳实践。因此它是一个服务于框架的框架,服务的范围是简化配置文件。

约定优于配置的体现

  1. maven 的目录结构
    a) 默认有 resources 文件夹存放配置文件
    b) 默认打包方式为 jar
  2. spring-boot-starter-web 中默认包含 spring mvc 相关依赖以及内置的 tomcat 容器,使得构建一个 web 应用更加简单
  3. 默认提供 application.properties/yml 文件
  4. 默认通过 spring.profiles.active 属性来决定运行环境时读取的配置文件
  5. EnableAutoConfiguration 默认对于依赖的 starter 进行自动装载

从@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}
)}
)

SpringBootApplication 本质上是由 3 个注解组成,分别是:

  • @SpringBootConfiguration --实际上就是@Configuration
  • @EnableAutoConfiguration
  • @ComponentScan

简单分析@SpringBootConfiguration

它是 JavaConfig形式的基于 Spring IOC 容器的配置类使用的一种注解。任何一个标注了@Configuration 的 Java 类定义都是一个JavaConfig 配置类。而在这个配置类中,任何标注了@Bean 的方法,它的返回值都会作为 Bean 定义注册到Spring 的 IOC 容器,方法名默认成为这个 bean 的 id。

简单分析@ComponentScan

它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到 spring 的 Ioc 容器中。
标识需 要装配的类的 形式主要是: @Component 、@Repository、@Service、@Controller 这类的注解标识的类。
ComponentScan 默认会扫描当前 package 下的的所有加了相关注解标识的类到 IoC 容器中;

简单分析@EnableAutoConfiguration

@EnableAutoConfiguration 的 主 要 作 用 其 实 就 是 帮 助springboot 应用把所有符合条件的@Configuration配置都加载到当前 SpringBoot 创建并使用的 IoC 容器中。其中最重要的就是@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 {};
}

Import 注解

import 注解是什么意思呢? 联想到 xml 形式下有一个 < import resource/> 形式的注解,就明白它的作用了。import 就是把多个容器配置合并在一个配置中。在JavaConfig 中所表达的意义是一样的。

深入分析 EnableAutoConfiguration

EnableAutoConfiguration 的 主 要 作 用 其 实 就 是 帮 助spring boot 应用把所有符合条件的@Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器中。再回到 EnableAutoConfiguration 这个注解中,我们发现它的 import 是这样的:

@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration{

@Import 注解可以配置三种不同的 class:

  1. 第一种就是基于普通 bean 或者带有@Configuration 的 bean 进行注入
  2. 实现 ImportSelector 接口进行动态注入
  3. 实现 ImportBeanDefinitionRegistrar 接口进行动态注入

@EnableAutoConfiguration 注解的实现原理

它会通过 import 导入第三方提供的 bean 的配置类:AutoConfigurationImportSelector
从名字来看,可以猜到它是基于 ImportSelector 来实现基于动态 bean 的加载功能。
定位到AutoConfigurationImportSelector 这个类中的selectImports 方法:

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

selectImports 返回的数组(类的全类名)都会被纳入到spring 容器中。
本质上来说,其实 EnableAutoConfiguration 会帮助springboot 应用把所有符合@Configuration 配置都加载到当前 SpringBoot 创建的 IoC 容器,而这里面借助了Spring 框架提供的一个工具类 SpringFactoriesLoader 的支持。以及用到了 Spring 提供的条件注解@Conditional,选择性的针对需要加载的 bean 进行条件过滤。

SpringFactoriesLoader

这里简单分析一下SpringFactoriesLoader 这个工具类的使用。它其实和java 中的 SPI 机制的原理是一样的,不过它比 SPI 更好的点在于不会一次性加载所有的类,而是根据 key 进行加载
SpringFactoriesLoader 的作用是从classpath/META-INF/spring.factories文件中,根据 key 来加载对应的类到 spring IoC 容器中。

条件过滤

在分析 AutoConfigurationImportSelector 的源码时,会先扫描 spring-autoconfiguration-metadata.properties文件,最后在扫描 spring.factories 对应的类时,会结合前面的元数据进行过滤,为什么要过滤呢? 原因是很多的@Configuration 其实是依托于其他的框架来加载的,如果当前的 classpath 环境下没有相关联的依赖,则意味着这些类没必要进行加载,所以,通过这种条件过滤可以有效的减少@configuration 类的数量从而降低SpringBoot 的启动时间。

Conditional 中的其他注解

Conditions描述
@ConditionalOnBean在存在某个 bean 的时候
@ConditionalOnMissingBean不存在某个 bean 的时候
@ConditionalOnClass当前 classpath 可以找到某个类型的类时
@ConditionalOnMissingClass当前 classpath 不可以找到某个类型的类时
@ConditionalOnResource当前 classpath 是否存在某个资源文件
@ConditionalOnProperty当前 jvm 是否包含某个系统属性为某个值
@ConditionalOnWebApplication当前 spring context 是否是 web 应用程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值