Spring Boot自动装配代码详解

  1. 概述

    • Spring Boot自动装配是其核心特性之一,它能够根据项目中添加的依赖自动配置Spring应用程序。通过自动装配,开发人员可以减少大量的配置工作,快速搭建起一个可用的Spring应用。
  2. 关键组件和注解

    • @SpringBootApplication注解
      • 这是Spring Boot应用的主注解,它是一个组合注解,实际上包含了@Configuration@EnableAutoConfiguration@ComponentScan三个注解。
      • @Configuration:表明这个类是一个配置类,用于定义Spring的Bean。在配置类中,可以通过@Bean注解方法来创建和配置Bean实例。例如:
        @Configuration
        public class AppConfig {
            @Bean
            public MyService myService() {
                return new MyServiceImpl();
            }
        }
        
        • 这里定义了一个名为myService的Bean,类型是MyService,其实现是MyServiceImpl
      • @EnableAutoConfiguration:这是自动装配的关键注解。它会启用Spring Boot的自动配置机制,告诉Spring Boot去根据项目的依赖和配置自动配置应用。它通过@Import注解导入了AutoConfigurationImportSelector类来实现自动配置的功能。
      • @ComponentScan:用于扫描指定包及其子包下的组件(如@Component@Service@Repository@Controller等注解标记的类),将它们注册为Spring的Bean。默认情况下,它会扫描主应用类所在的包及其子包。例如,如果主应用类在com.example.myapp包下,那么@ComponentScan会扫描com.example.myapp及其所有子包下的组件。
    • AutoConfigurationImportSelector
      • 这个类是自动装配的核心实现类。它实现了ImportSelector接口,该接口的selectImports方法用于返回要导入的配置类的全限定名数组。
      • AutoConfigurationImportSelector中,selectImports方法会从META - INF/spring.factories文件中读取自动配置类的列表。它通过SpringFactoriesLoader.loadFactoryNames方法来加载这些配置类,例如:
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
        
      • 这里的getAutoConfigurationEntry方法会获取需要自动导入的配置类,这些配置类是根据项目中的依赖和条件来确定的。
  3. META - INF/spring.factories文件机制

    • 这是自动装配的重要配置文件。在Spring Boot的各个依赖中,都可以包含META - INF/spring.factories文件。
    • 这个文件的格式是key = value的形式,其中一个关键的配置项是org.springframework.boot.autoconfigure.EnableAutoConfiguration。它的值是一个自动配置类的列表,例如:
      org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
        org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
        // 其他自动配置类
      
    • 当Spring Boot启动时,AutoConfigurationImportSelector会读取这些文件,找到所有的自动配置类。然后,根据条件注解(如@ConditionalOnClass@ConditionalOnMissingBean等)来判断这些自动配置类是否应该被应用。
    • 条件注解示例
      • @ConditionalOnClass:这个注解用于判断某个类是否在类路径上。例如,DataSourceAutoConfiguration中有如下注解:
        @Configuration
        @ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
        public class DataSourceAutoConfiguration {
            // 配置内容
        }
        
        • 这表示只有当DataSource类和EmbeddedDatabaseType类都在类路径上时,DataSourceAutoConfiguration这个自动配置类才会被应用。这样可以确保只有在项目中添加了相关的数据源依赖时,才会进行数据源的自动配置。
      • @ConditionalOnMissingBean:用于判断某个类型的Bean是否不存在。例如,在某个自动配置类中定义一个方法来配置一个Bean:
        @Bean
        @ConditionalOnMissingBean
        public MyBean myBean() {
            return new MyBeanImpl();
        }
        
        • 这表示只有当容器中不存在MyBean类型的Bean时,才会创建并添加MyBeanImpl这个Bean到容器中。
  4. 自动配置的过程

    • Spring Boot应用启动时,首先会加载主应用类,由于@SpringBootApplication注解的存在,@Configuration注解使得这个类被视为一个配置类,@ComponentScan开始扫描组件。
    • 同时,@EnableAutoConfiguration触发自动装配过程。AutoConfigurationImportSelector从各个依赖的META - INF/spring.factories文件中获取自动配置类列表。
    • 然后,对于每个自动配置类,根据条件注解来检查是否满足应用条件。如果满足条件,就会将这个自动配置类加载到Spring容器中,自动配置类中的@Bean方法会被调用,创建和配置相应的Bean,从而完成自动装配的过程。

具体源码讲解

  1. @SpringBootApplication注解源码分析

    • 首先查看@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 = AutoExcludeFilter.class)
    })
    public class SpringBootApplication {
    }
    
    • 可以看到它是一个组合注解,包含了@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan
    • @SpringBootConfiguration:它实际上就是@Configuration注解,用于标识这个类是一个配置类。
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public class SpringBootConfiguration {
    }
    
    • @EnableAutoConfiguration:这是自动装配的关键。它的源码如下:
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public class EnableAutoConfiguration {
    }
    
    • 其中@Import(AutoConfigurationImportSelector.class)是核心部分。这个AutoConfigurationImportSelector类用于加载自动配置类。
    • @ComponentScan:用于扫描组件,它有一些默认的扫描规则和可以自定义的过滤规则,如上述代码中的excludeFilters,用于排除某些类型的组件扫描。
  2. AutoConfigurationImportSelector源码分析

    • AutoConfigurationImportSelector实现了ImportSelector接口,其中关键的方法是selectImports
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    
    • 这个方法首先检查自动配置是否启用。然后通过AutoConfigurationMetadataLoader.loadMetadata加载自动配置元数据,再通过getAutoConfigurationEntry获取自动配置项。
    • 深入getAutoConfigurationEntry方法:
    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                                                                AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    
    • 在这里,getCandidateConfigurations方法用于获取候选的自动配置类列表,它会从META - INF/spring.factories文件中读取相关配置:
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                                           getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto - configuration classes found in META - INF/spring.factories. If you " +
                                          "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
    
    • SpringFactoriesLoader.loadFactoryNames方法会读取META - INF/spring.factories文件,查找org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类列表。
  3. META - INF/spring.factories文件读取源码分析

    • SpringFactoriesLoader类用于读取spring.factories文件,关键方法是loadFactoryNames
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    
    • 它调用了loadSpringFactories方法:
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result!= null) {
            return result;
        }
        try {
            Enumeration<URL> urls = (classLoader!= null?
                                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<Object, Object> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        } catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                                               FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
    
    • 这个方法首先检查缓存中是否已经有读取的结果。如果没有,就通过ClassLoader获取META - INF/spring.factories文件的URL,然后将其内容读取到Properties对象中。最后,将文件中的配置解析出来,以key - value的形式存储在MultiValueMap中,其中key是配置项的类型(如org.springframework.boot.autoconfigure.EnableAutoConfiguration),value是对应的配置类列表。
  4. 条件注解的源码体现(以@ConditionalOnClass为例)

    • @ConditionalOnClass注解用于判断某个类是否在类路径上。它的实现基于Condition接口。
    • 当Spring容器在处理自动配置类时,会检查条件注解。@ConditionalOnClass对应的Condition实现类是OnClassCondition。在自动配置类加载过程中,会调用OnClassConditionmatches方法来判断条件是否满足:
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 省略部分代码
        List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
        if (onClasses!= null) {
            List<String> missing = filter(onClasses, ClassNameFilter.MISSING, context);
            if (!missing.isEmpty()) {
                return false;
            }
        }
        List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
        if (onMissingClasses!= null) {
            List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, context);
            if (!present.isEmpty()) {
                return false;
            }
        }
        return true;
    }
    
    • 这个方法会获取@ConditionalOnClass注解中指定的类列表,然后检查这些类是否在类路径上。如果有任何一个指定的类不存在,就返回false,表示条件不满足,自动配置类不会被加载。通过这样的机制,Spring Boot可以根据类的存在与否来决定自动配置类的加载与否,实现智能的自动装配。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950522.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android车载音频系统目录

目录 第一章 1.1 Android Automotive&#xff08;一&#xff09; 1.2 Android Automotive&#xff08;二&#xff09; 1.3 Android Automotive&#xff08;三&#xff09; 第二章 2.1 Android车载音频系统概览 2.2 车载音频焦点 2.3 车载音频配置 2.4 Audio control HAL…

怎么管理电脑usb接口,分享四种USB端口管理方法

怎么管理电脑usb接口&#xff0c;分享四种USB端口管理方法 USB接口作为电脑重要的外部接口&#xff0c;方便了数据传输和设备连接。 然而&#xff0c;不加管理的USB接口也可能带来安全隐患&#xff0c;例如数据泄露、病毒传播等。 因此&#xff0c;有效管理电脑USB接口至关重…

React+redux项目搭建流程

1.创建项目 create-react-app my-project --template typescript // 创建项目并使用typescript2.去除掉没用的文件夹&#xff0c;只保留部分有用的文件 3.项目配置&#xff1a; 配置项目的icon 配置项目的标题 配置项目的别名等&#xff08;craco.config.ts&…

conda+jupyter+pycharm:如何在Windows conda环境下运行jupyter并使用浏览器或者pycharm运行.ipynb

1 安装conda 2 conda环境下安装jupyter pip install jupyter3 设置jupyter配置文件 1&#xff09;创建 jupyter_notebook_config.py文件 jupyter notebook --generate-config 2&#xff09;设置密码 3&#xff09;设置参数 直接将以下参数修改为自己的配置后复制到配置文件…

微信小程序获取图片使用session(上篇)

概述&#xff1a; 我们开发微信小程序&#xff0c;从后台获取图片现实的时候&#xff0c;通常采用http get的方式&#xff0c;例如以下代码 <image class"user_logo" src"{{logoUrl}}"></image>变量logoUrl为ur图片l的请求地址 但是对于很多…

【江协STM32】9-1/2/3 USART串口协议、USART外设、串口发送串口发送+接收

1. 通信接口 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统通信协议&#xff1a;制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发全双工&#xff1a;指通信双方能够同时进行双向通信。发送线路和接收线路互不影响&#xff0c…

第一 二章 小车硬件介绍-(全网最详细)基于STM32智能小车-蓝牙遥控、避障、循迹、跟随、PID速度控制、视觉循迹、openmv与STM32通信、openmv图像处理、smt32f103c8t6

第一篇-STM32智能小车硬件介绍 后续章节也放这里 持续更新中&#xff0c;视频发布在小B站 里面。这边也会更新。 B站视频合集: STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随 小B站链接:https://www.bilib…

【网络】电路交换(Circuit Switching)、报文交换(Message Switching)和分组交换(Packet Switching)

电路交换&#xff08;Circuit Switching&#xff09;&#xff1a;一条专用的通信线路&#xff08;或电路&#xff09;&#xff08; 电话专用线路&#xff0c;好处&#xff1a;专用稳定&#xff0c;有没有数据都被占用&#xff0c;坏处&#xff1a;容易浪费&#xff09; 报文交换…

Pixel 6a手机提示无法连接移动网络,打电话失败!

1、开启VoLTE 2、如果没有&#xff0c;下载shizuku和PixelIMS应用。 shizuke Releases RikkaApps/Shizuku GitHub PixellMS Release v1.2.8 kyujin-cho/pixel-volte-patch GitHub 3、安装shizuke启动&#xff0c;开通root可以直接点击下面的启动&#xff0c;如果没有就…

游戏关卡设计的常用模式

游戏关卡分为很多种&#xff0c;但常用的有固定套路&#xff0c;分为若干种类型。 关卡是主角与怪物、敌方战斗的场所&#xff0c;包括装饰物、通道。 单人游戏的关卡较小&#xff0c;偏线性&#xff1b; 联机/MMO的关卡较大&#xff0c;通道多&#xff0c;自由度高&#xf…

DC/AC并网逆变器模型与仿真MATLAB

DC/AC并网逆变器是一种将直流电&#xff08;DC&#xff09;转化为交流电&#xff08;AC&#xff09;&#xff0c;并将其与电网并联的设备。它的核心功能是实现直流电源&#xff08;如光伏电池板或储能电池&#xff09;与电网的有效连接&#xff0c;同时保证输出电能质量满足电网…

作业:IO:day2

题目一 第一步&#xff1a;创建一个 struct Student 类型的数组 arr[3],初始化该数组中3个学生的属性 第二步&#xff1a;编写一个叫做save的函数&#xff0c;功能为 将数组arr中的3个学生的所有信息&#xff0c;保存到文件中去&#xff0c;使用fread实现fwrite 第三步&#xf…

环动科技平均售价波动下滑:大客户依赖明显,应收账款周转率骤降

《港湾商业观察》施子夫 2024年12月18日&#xff0c;浙江环动机器人关节科技股份有限公司&#xff08;以下简称&#xff0c;环动科技&#xff09;的上市审核状态变更为“已问询”&#xff0c;公司在11月25日科创板IPO获上交所受理&#xff0c;独家保荐机构为广发证券。 此次环…

【数据可视化-11】全国大学数据可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

SAP 02-AMDP Functions for CDS Table Functions

1. 创建一个Core Data Service Table Functions 新建 Core Data Service Table Function 定义CDS Table Functions EndUserText.label: a simple AMDP for CDS Table Functions ClientDependent: true //打开 Open SQL 的自动客户端处理 defin…

Ungoogled Chromium127 编译指南 MacOS篇(八)- 开始编译

1. 引言 完成了所有依赖包的安装后&#xff0c;我们终于来到了最关键的编译阶段。在开始编译之前&#xff0c;有一些重要的配置信息需要了解。本文将指导您完成整个编译过程。 2. 签名相关说明 虽然在我们的测试编译中不需要进行签名操作&#xff0c;但了解官方的签名要求仍…

SpringBootWeb案例-1(day10)

准备工作 需求 & 环境搭建 需求说明 环境搭建 步骤&#xff1a; 准备数据库表(dept、emp)创建 springboot 工程&#xff0c;引入对应的起步依赖&#xff08;web、mybatis、mysql 驱动、lombok&#xff09;配置文件 application.properties 中引入 mybatis 的配置信息&…

动手学深度学习-卷积神经网络-1从全连接层到卷积

目录 不变性 多层感知机的限制 平移不变性 局部性 卷积 “沃尔多在哪里”回顾 通道 小结 我们之前讨论的多层感知机十分适合处理表格数据&#xff0c;其中行对应样本&#xff0c;列对应特征。 对于表格数据&#xff0c;我们寻找的模式可能涉及特征之间的交互&#xff0…

【HTML+CSS+JS+VUE】web前端教程-4-标签之段落、换行、水平线

标签之段落 段落是通过<p>标签定义的换行 如果您希望在不生产一个新段落的情况下进行换行&#xff08;新行&#xff09;&#xff0c;请使用<br><br />元素是一个空的HTML元素 <p>这个<br>段落<br>演示了分行的效果</p>水平线 <…

vulnhub靶场【DC系列】之7

前言 靶机&#xff1a;DC-7&#xff0c;IP地址为192.168.10.13 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.2 都采用VMWare&#xff0c;网卡为桥接模式 对于文章中涉及到的靶场以及工具&#xff0c;我放置在网盘中&#xff0c;链接&#xff1a;https://pan.quark…