源码角度解析SpringBoot 自动配置

news2024/11/26 2:35:35

文章目录

  • 前言
  • 一、了解相关注解
    • 1.@Condition注解
    • 2.@Enable注解
  • 二、SpringBoot自动配置
    • 1.@SpringBootApplication注解
    • 2.@SpringBootConfiguration注解
    • 3.@EnableAutoConfiguration注解
    • 4.@Conditional注解
  • 总结


前言

Spring Boot 自动配置是 Spring Boot 的核心特性之一,它的目标是通过分析应用程序的类路径(classpath)和依赖关系,来自动配置 Spring 应用程序所需的 Bean、设置和组件。这意味着开发人员不需要手动配置大部分常见的 Spring 组件,Spring Boot 将根据环境和依赖来进行智能配置。这样,开发人员可以专注于编写业务逻辑,而不必过多关心底层配置细节。


一、了解相关注解

1.@Condition注解

Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。
SpringBoot 提供的常用条件注解:

  • @ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
  • @ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
  • @ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
  • @ConditionalOnBean:判断环境中有对应Bean才初始化Bean

2.@Enable注解

SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。
@Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:​
① 导入Bean;
@Import(User.class)
② 导入配置类;
@Import(UserConfig.class)
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类;
@Import(MyImportSelector.class)
④ 导入 ImportBeanDefinitionRegistrar 实现类。
@Import({MyImportBeanDefinitionRegistrar.class})

二、SpringBoot自动配置

1.@SpringBootApplication注解

主启动类注解:

package org.springframework.boot.autoconfigure;
@Targer(ElementType.TYPE)//作用于类
@Retention(Retention.RUNTIME)//该注解在运行时可见,可以通过反射读取
@Documented//文档相关
@Inherited//可以被子类继承
@SpringBootConfiguration//标记该类为 Spring Boot 的配置类
@EnableAutoConfiguration//实现自动配置功能
@ComponentScan(excludeFilters = {
        @Filter(type=FilterType.CUSTOM,classes = TypeExcludeFilter.class),
        @Filter(type=FilterType.CUSTOM,classes = AutoConfigurationExcludeFilter.class)
})//配置组件扫描,以查找和注册 Spring Bean
public @interface SpringBootApplication{……}

总的来说,大概可以把@SpringBootApplication看作是@Configuration、@EnableAutoConfigeration、@ComponentScan注解的集合。
@EnableAutoConfigeration:启用SpringBoot的自动配置机制;
@ComponentScan:扫描被@Component(@Service,@Controller)注解的bean,注解默认会扫描该类所在包下的所有的类;
@Configuration:允许在上下文中注册额外的bean或倒入其他配置类。

2.@SpringBootConfiguration注解

SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

//这里的@Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
@Configuration
public @interface SpringBootConfiguration {}
//里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用
@Component
public @interface Configuration {....}

3.@EnableAutoConfiguration注解

@EnableAutoConfiguration开启自动配置功能,它可以说是我们研究SpringBoot 自动配置的重中之重了:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {.....}

3.1@AutoConfigurationPackage注解:

//AutoConfigurationPackage的子注解
//Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

在默认的情况下就是将:主配置类(@SpringBootApplication)的所在包及其子包里边的组件扫描到Spring容器中。

3.2@Import({AutoConfigurationImportSelector.class})注解:给容器导入组件
AutoConfigurationImportSelector :自动配置导入选择器,给容器中导入一些组件;
通过了@import注解导入了AutoConfigurationImportSelector这个配置类,根据类名可以发现,这个实现类是@Import提供4中用法中的ImportSelector的实现类,那么它就一定实现了selectImports这个个方法

String[] selectImports(AnnotationMetadata importingClassMetadata);

该方法的主要目的是根据运行时的条件或配置动态地确定要导入哪些配置类,以扩展或自定义 Spring 应用程序的配置。
返回值是一个字符串数组,其中包含需要导入到当前配置类中的其他配置类的全限定类名。
那我们就来看看他是怎么来重写这个方法的:

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

this.isEnabled(annotationMetadata) 方法检查是否启用了自动配置。如果没有启用,就返回一个空数组 NO_IMPORTS,表示不导入任何配置类。
getAutoConfigurationEntry(annotationMetadata) 方法来获取自动配置入口,那么他是如何去获取的呢?继续进方法看看:

List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

用于根据给定的注解元数据 (annotationMetadata) 和属性 (attributes) 获取潜在的自动配置类的列表,它的作用就是找到所有可能与应用程序上下文相关的自动配置类。
↓ ↓ ↓

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
        ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

这个方法想告诉我们什么呢,其实看到这里官方给出的提示就已经很明显了:

在META-INF/spring中找不到自动配置类。在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中。如果您正在使用自定义打包,请确保该文件是正确的。

简但来说,就是在所有包名叫做autoConfiguration的包下面都有META-INF/spring.factories文件,Spring启动的时候会扫描这个文件,将其文件包装成Properties对象从Properties对象获取到key值为EnableAutoConfiguration的数据,然后添加到容器里边。

在这里插入图片描述

4.@Conditional注解

现在SpringBoot自动配置信息有了,自动配置还差什么呢?
到这一步为止,SpringBoot已经为我们的项目中导入了大量的自动配置类,且项目中的自动配置类全部执行,但是这133个自动配置类我们都会用到吗?
以RedisAutoConfiguration为例:
在这里插入图片描述
看到这里就不难发现,虽然SpringBoot帮我们加载了Redis的自动配置类,但并没有把redisTemplate模板类帮我们注入容器,真正想要注入模板类到容器里供我们使用之前,还要经过一些选择注解的判断且全部成功才可以成功注入。
以redisTemplate模板类为例,它想要成功注入,就得经过三个选择注解的全部成功才可以注入:

//项目中必须要有RedisProperties这个类才可以生效(坐标中倒入)
@EnableConfigurationProperties({RedisProperties.class})
//容器中必须没有一个Bean为redisTemplate,这也就允许我们自己向容器中注入一个redisTemplate!
@ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
 //指定必须是单例工厂才能生效
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)

只有在经过这三个选择注解后,自动装配才算真正完成,redisTemplate类才能供我们在项目中使用。


总结

  • @EnableAutoConfiguration 注解内部使用@Import(AutoConfigurationImportSelector.class)来加载配置类;
  • 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot应用启动时,会自动加载这些配置类,初始化Bean;
  • 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无人化在线静电监控系统的组成

无人化在线静电监控系统是一种用于检测和监控静电情况的系统&#xff0c;它可以自动地实时监测各个区域的静电水平&#xff0c;并在出现异常情况时发出报警信号。静电监控报警器则是该系统中的一个重要组成部分&#xff0c;用于接收和传达报警信号。 无人化在线静电监控系统通…

【人工智能】—局部搜索算法、爬山法、模拟退火、局部剪枝、遗传算法

文章目录 局部搜索算法内存限制局部搜索算法示例&#xff1a;n-皇后爬山算法随机重启爬山模拟退火算法局部剪枝搜索遗传算法小结 局部搜索算法 在某些规模太大的问题状态空间内&#xff0c;A*往往不够用 问题空间太大了无法访问 f 小于最优的所有状态通常&#xff0c;甚至无法储…

SpringMVC入门篇

目录 1.SpringMVC工作流程 2.SpringMVC核心组件 2.1 DispatcherServlet 2.2 HandlerMapping 2.3 Handler 2.4 HandlerAdapter 2.5 ViewResolver 2.6 View 3.SpringMVC的入门 3.1 添加相关依赖 3.2 创建Spring-mvc.xml 3.3 配置web.xml 3.4 效果演示 4.静态资源处…

静态路由——实现两个不相连的网段通信实验

路漫漫其修远兮&#xff0c;吾将上下而求索 今天做一个简单的实现两个不相连的网段通信实验&#xff0c;本实验使用静态路由配置&#xff0c;主要 加强初学者对静态路由的理解。 实际中不可能只使用静态路由&#xff0c;还要使用诸多的其他网络协议&#xff0c;达到安全可靠的…

揭榜!9家行业代表性企业获得软件/智舱/车联细分赛道标杆奖

过去几年&#xff0c;在特斯拉及新势力的带动下&#xff0c;车企的盈利模式正在寻求从“一次售卖”转变为“硬件预埋&#xff0b;软件付费解锁”&#xff0c;背后是驱动汽车软件架构的迭代&#xff0c;即从面向信号的软件架构&#xff0c;过渡至面向服务的SOA架构。 同时&#…

大厂面试 | 百度一面,顶不住

题目来源&#xff1a;https://www.nowcoder.com/feed/main/detail/d39aabc0debd4dba810b4b9671d54348 前文 本期是【捞捞面经】系列文章的第 2 期&#xff0c;持续更新中…。&#xff08;更多与往期下方仓库直达&#xff09; 《捞捞面经》系列正式开始连载啦&#xff0c;据说看…

Liunx远程调试

1、Vscode中使用xdebug调试php 2、工具的下载 3、debug的配置 1、Vscode中使用xdebug调试php 1&#xff0c;在phpstudy中启用xdebug扩展 2&#xff0c;打开php.ini&#xff0c;修改配置 [Xdebug] zend_extensionD:/PHP/Extensions/php/php5.6.9nts/ext/php_xdebug.dll xdebug…

不关闭Tamper Protection(篡改保护)下强制卸载Windows Defender和安全中心所有组件

个人博客: xzajyjs.cn 背景介绍 由于微软不再更新arm版本的win10系统&#xff0c;因此只能通过安装insider preview的镜像来使用。而能找到的win10 on arm最新版镜像在安装之后由于内核版本过期&#xff0c;无法打开Windows安全中心面板了&#xff0c;提示如下&#xff1a; 尝…

伯俊ERP与金蝶云星空对接集成表头表体组合查询连通分布式调出单新增(调拨出库对接分布式调出(KD调拨)6月)

伯俊ERP与金蝶云星空对接集成表头表体组合查询连通分布式调出单新增(调拨出库对接分布式调出&#xff08;KD调拨&#xff09;6月) 对接系统&#xff1a;伯俊ERP 伯俊科技&#xff0c;依托在企业信息化建设方面的领先技术与实践积累&#xff0c;致力于帮助企业实现全渠道一盘货。…

【数据结构——有向图】有环无环判定、拓扑排序(DFS、BFS)

文章目录 1. 什么是有向图2. 什么是拓扑排序2. 有向图的拓扑排序2. 1 BFS 广度优先2. 2 DFS 深度优先 3. 有向图有环无环判定 1. 什么是有向图 有向图&#xff08;Directed Graph&#xff09;&#xff0c;也被称为有向图形或方向图&#xff0c;是一种图的类型。在有向图中&…

联发科MTK6765处理器参数_4G安卓核心板主板定制方案

MT6765安卓核心板是一款基于MTK平台的高性能智能模块&#xff0c;是一款工业级的产品。该芯片也被称为Helio P35。MT6765核心板是目前市场上最受欢迎的低成本智能芯片之一&#xff0c;其卓越的性能和创新技术为用户提供了更加顺畅和高效的使用体验。 MTK6765&#xff08;曦力 …

h5开发网站-css实现页面的背景固定定位

一、需求&#xff1a; 在页面滚动时&#xff0c;背景图片保持不变&#xff0c;而不是跟随滚动。 二、解决方式&#xff1a; 使用背景固定定位&#xff0c;只需要在CSS中增加一个background-attachment: fixed;属性即可。 具体代码&#xff1a; <div class"item_right…

String.format() 格式化字符串的方法, 不同占位符表示的含义及使用方式

学习目标&#xff1a; 目标如下&#xff1a; String.format() 格式化字符串的方法&#xff0c; 不同占位符表示的含义及使用方式 学习内容&#xff1a; 内容&#xff1a; 占位符类型 String.format()方法是一种格式化字符串的方法 字符串&#xff1a;一个占位符"%s&q…

Spring Cloud Alibaba-Feign整合Sentinel

第1步: 引入sentinel的依赖 <!--sentinel客户端--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> 第2步: 在配置文件中开启Feign对Sentinel的…

面试题查漏补缺 i++和 ++ i哪个效率更高

i 和 i 哪个效率更高&#xff1f; 在这里声明&#xff0c;简单地比较前缀自增运算符和后缀自增运算符的效率是片面的&#xff0c;因为存在很多因素影响这个问题的答案。首先考虑内建数据类型的情况:如果自增运算表达式的结果没有被使用&#xff0c;而是仅仅简单地用于增加一员…

2023-09-05 LeetCode每日一题(从两个数字数组里生成最小数字)

2023-09-05每日一题 一、题目编号 2605. 从两个数字数组里生成最小数字二、题目链接 点击跳转到题目位置 三、题目描述 给你两个只包含 1 到 9 之间数字的数组 nums1 和 nums2 &#xff0c;每个数组中的元素 互不相同 &#xff0c;请你返回 最小 的数字&#xff0c;两个数…

Python 遍历字典的若干方法

字典是 Python 的基石。这门语言的很多方面都是围绕着字典构建的 模块、类、对象、globals()和 locals() 都是字典与 Python 实现紧密联系的例子 以下是 Python 官方文档定义字典的方式&#xff1a; An associative array, where arbitrary keys are mapped to values. The k…

机器人中的数值优化(十二)——带约束优化问题简介、LP线性规划

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

移动端App持续集成体系构建实战

这里写目录标题 一、目标1、前言2、优势&#xff1a;3、涉及技术点4、目标 二、测试app构建、打包过程1、安卓打包的环境要求 三、演示安卓源码打包四、演示安卓App部署1、前提条件2、命令控制apk安装与卸载 五、安卓UI自动化测试1、Appium app自动化测试-Python2、实现的验证点…

Vue3+Ts实现父子组件间传值的两种方式

文章目录 写在前面1、v-modelemit传值1.1父向子传递数据1.2子向父传递数据 2、使用v-bindemit2.1父向子传递数据2.2子向父传递数据 总结 写在前面 对于常用的组件间传参最近有了有点小心得总结一下&#xff0c;主要是两种子向父组件传参的方式总结。欢迎评论区讨论 概览&…