从源码层面深入分析,揭秘Spring Boot自动配置

概述


Spring Boot 是一个用于创建独立的、生产级别的 Spring 应用程序的框架。它极大地简化了 Spring 应用程序的开发过程,其中一个关键的功能就是自动配置(Auto-Configuration)。

自动配置可以根据项目需求自动配置各种服务和组件,它可以帮助开发者在不需要显式配置的情况下,快速地构建一个运行的应用程序。

自动配置是 Spring Boot 的一个核心特性,它通过分析项目的依赖和环境,自动地为应用程序配置 Spring 配置文件。这意味着开发者无需手动配置大量的 Spring Bean,Spring Boot 会根据环境和项目的依赖来智能地为应用程序创建所需的 Bean。

原理

Spring Boot的自动配置原理基于Java的反射机制和Spring的IoC容器。它通过读取项目中的配置文件(如application.properties或application.yml),根据配置信息自动创建和配置各种服务。

可以概括为以下几个步骤

Spring Boot Starter


Spring Boot 项目通常依赖于所谓的 "Starters",这是一组预定义了常用库的依赖集合。Starters 包含了应用程序所需的所有依赖,比如 Web Starter 会包含用于构建 Web 应用程序的所有必要库。

spring.factories 文件


在每个 Starter 的 META-INF/spring.factories 文件中,定义了一系列的自动配置类。这些自动配置类会在应用程序启动时被 Spring Boot 自动扫描和加载。

@ConditionalOnX 注解


在自动配置类中,通常会使用 @ConditionalOnX 注解来控制该自动配置是否生效。这个注解会根据特定的条件(比如类是否在类路径中、特定的 Bean 是否存在等)来决定是否应用这个配置。

配置 Bean


自动配置类会定义一些必要的 Bean,并使用 @Configuration 注解将它们标记为配置类。这些 Bean 会在 Spring 上下文中被自动注册。

配置属性


自动配置类还可以通过读取 application.propertiesapplication.yml 文件中的属性来自定义它们的行为。这样,开发者可以根据需要进行定制。

源码剖析

Spring Boot 应用的启动类一般均位于src/main/java根路径下

java 复制代码
@SpringBootApplication
public class Application {

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

@SpringBootApplication开启组件扫描和自动配置,而SpringApplication.run则负责启动引导应用程序。@SpringBootApplication是一个复合Annotation,它将三个有用的注解组合在一起:

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 {
    // ...省略内容
}

@SpringBootConfiguration就相当于@Configuration,它是 Spring 框架的注解,标明该类是一个JavaConfig配置类。而@ComponentScan启用组件扫描。

@EnableAutoConfiguration注解表示开启 Spring Boot 自动配置功能,Spring Boot 会根据应用的依赖、自定义的 bean、classpath 下有没有某个类 等等因素来猜测你需要的 bean,然后注册到 IOC 容器中。@EnableAutoConfiguration是如何推算出需求的?首先来看下它的定义:

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // ...省略内容
}

重点关注点在@Import({AutoConfigurationImportSelector.class}) @Import注解用于导入类,并将这个类作为一个 bean 的定义注册到容器中,这里它将把EnableAutoConfigurationImportSelector作为 bean 注入到容器中,而这个类会将所有符合条件的@Configuration 配置都加载到容器中,查看EnableAutoConfigurationImportSelector代码可以看到:

AutoConfigurationImportSelector 实现了DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered 这些接口。核心代码为

java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    //...
    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);
        }
    }

annotationMetadata 参数是一个元数据接口,它提供了对注解元数据的访问权限,通常用于在运行时检查类上的注解。

getAutoConfigurationEntry(annotationMetadata)方法会根据传入的 annotationMetadata返回一个 AutoConfigurationEntry 对象。

AutoConfigurationEntry 是一个内部类,用于封装自动配置项的信息,包括该自动配置项的类名、条件(Conditions)等信息。

通过获取到的 autoConfigurationEntry 对象,Spring Boot 就能够知道哪些自动配置类需要被导入,从而将它们注册到 Spring 上下文中。

相关推荐
一二小选手几秒前
【高级编程】XML DOM4J解析XML文件(含案例)
xml·java
终末圆2 分钟前
MyBatis XML映射文件编写【后端 18】
xml·java·开发语言·后端·算法·spring·mybatis
就这个java爽!2 分钟前
超详细的XML介绍【附带dom4j操作XML】
xml·java·开发语言·数据库·青少年编程·eclipse
kunkun1014 分钟前
Mybatis的XML实现方法
xml·java·mybatis
码农小伙7 分钟前
SpringBoot中基于Mybatis-Plus多表联查(无xml,通过注解实现)
xml·spring boot·mybatis
libai10 分钟前
STM32 USB HOST CDC 驱动CH340
java·前端·stm32
IT学长编程11 分钟前
计算机毕业设计 数字化农家乐管理平台的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·毕业论文·计算机毕业设计选题·计算机毕业设计开题报告·农家乐管理平台
Q1860000000012 分钟前
用java语言写一个表的查询操作
java·开发语言·oracle
2401_8576009512 分钟前
心理教育辅导系统的设计与Spring Boot实现
java·spring boot·后端
GGBondlctrl14 分钟前
【后端开发】JavaEE初阶—线程的理解和编程实现
java·java-ee·线程的创建方式·线程是什么·线程的内部原理