【Spring Boot 原理分析】- 自动配置

news/2024/4/19 6:50:49/文章来源:https://blog.csdn.net/qq_33827423/article/details/129143567

【Spring Boot 原理分析】- 自动配置

Condition 注解

Condition 是 Spring 4.0 增加的条件判断功能,通过这个功能可以实现选择的创建 Bean 操作

👑 我们在使用 Spring 的时候,只需导入某个依赖的坐标,就可以直接通过 Autwired 注解,进行依赖注入,那我们有没有想过,我们的 Spring 是怎么知道,我们要将那个对象注入到容器中呢?

就比如 SpringBoot 是如何知道要创建的 RedisTemplate 的呢?

好,那我们就带着这个问题向下继续看。

  • 导入坐标
<!--导入 data-redis 坐标 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.7.2</version>
</dependency>
  • 获取 redisTemplate 对象
@SpringBootApplication
public class SpringBootDomeApplication {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringBootDomeApplication.class, args);//获取容器中的 redisTemplate 对象Object redisTemplate = run.getBean("redisTemplate");System.out.println(redisTemplate);}}

image-20230220222233254

可以看到最终输出打印了容器中获取的 redisTemplate 对象。

而当我们将 spring-boot-start-data-redis 坐标从依赖中去除时,当我们再次执行,肯定会报出一个 NoSuchBeanDefinitionException 的异常,如下图所示。

image-20230220222955471

💡问题就是,我们的 Spring 是怎么知道,我们到底有没有导入 redisTemlate 这个的起步依赖的呢?

我们通过一个案例,对上面的这种情况进行一个分析

📒 在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求如下:

  1. 导入 Jedis 坐标后,加载该 Bean ,没导入,则不加载

首先我们的创建一个 User 实体对象,然后添加一个 UserConfig 的配置类,让 Spring 启动的时候,自动将我们的 User 对象注入到我们的容器当中。

public class User {
}
@Configuration
public class UserConfig {@Beanpublic User user(){return new User();}
}

然后在启动类中修改 getBean 的参数,获取我们注入的 User 对象

@SpringBootApplication
public class SpringBootDomeApplication {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringBootDomeApplication.class, args);//获取容器中的 redisTemplate 对象Object redisTemplate = run.getBean("user");System.out.println(redisTemplate);}}

image-20230220224145104

为了控制我们的 Spring 在创建注入 User 到我们的 Spring 容器中,我在 UserConfig 的配置类上加一个 Condition 的注解。

@Configuration
public class UserConfig {@Bean@Conditional()public User user(){return new User();}
}

我们进入这个注解,然后看看这个注解都做了什么事呢?

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {Class<? extends Condition>[] value();
}

可以看到改注解中有一个 Condition 的参数,进入 Condition 这个接口中,可以看到我们需要给该注解提供一个Condition 的类对象 ,还有一个返回值为 Boolean 类型的方法matches

    /*** 确定条件是否匹配。* 参数:* context – 条件上下文 metadata – 正在检查的 class OR method 的元数据* 返回:* true 如果条件匹配并且可以注册组件,或者 false 否决注释组件的注册*/
@FunctionalInterface
public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

其实看到这里大概就已经明白了,官方解释原来就是说,如果 matches 返回的是一个 true 那么就注入到容器当中,负责就不注入。

所以我们得首先有一个用于实现 Condition 接口的实现类,并对该接口中的 matches 方法提供相应的实现,然后将该实现类文件对象,放置到我们需要加载的类上的 Condition 注解上。

public class ClassCondtion implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return false;}
}
@Configuration
public class UserConfig {@Bean@Conditional(ClassCondtion.class)public User user(){return new User();}
}

默认情况下返回的是 false ,也就是说默认情况下是对象是在 Spring 初始化的时候,不注入到容器中的。

所以我们现在再次运行查看控制台输出。可以看到控制台报出一个 Bean 找不到的错误

image-20230220230550243

我们将上面的 ClassCondition 类做略微的修改,再看看运行的情况。

public class ClassCondtion implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {try {//获取是否导入了该类Class<?> aClass = Class.forName("org.springframework.data.redis.core.RedisTemplate");} catch (ClassNotFoundException e) {return false;}return true;}
}

我们将 pom 文件中的 spring-boot-starter-data-redis 取消,我们的 User 对象则无法成功的注入到容器当中。

image-20230220231452401

所以通过上面的案例,其实我们就明白了,原来我们的 Spring 在进行启动的时候,会先通过 Condition 对象进行判断,如果注入的条件满足(坐标导入),则将对象注入到容器中,否则不进行注入。

Condition 注解的动态控制

⚠️ 上面的案例中有一个明显的问题,我们在 Condition 的实现类中是采用硬编码的方式,将包路径写在入了 实现类中。那有没有办法通过注解方式,自己指定要扫描的包呢?

其实是可以,这就需要我自定义注解,然后组合我们的 Condition注解,在自定义的注解中,定义一个用于存储路径的 value 变量,具体的实现步骤我们接着往下看。

  • 自定义 ConditionOnClass 注解
@Target({ElementType.TYPE, ElementType.METHOD}) //注解可加的范围
@Retention(RetentionPolicy.RUNTIME) //注解生效时机
@Documented //生成 Java document 文档
@Conditional(ClassCondtion.class)
public @interface ConditionOnClass {//定义一个用于存储扫描的路径的变量String[] value();
}
  • 通过参数获取注解中的属性值
package com.peggy.springbootdome.condtion;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.type.AnnotatedTypeMetadata;import java.util.Map;public class ClassCondtion implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {try {//获取注解注解中的所有的属性值Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());String[] value = (String[]) attributes.get("value");for (String s : value) {//获取是否导入了该类Class<?> aClass = Class.forName(s);}} catch (ClassNotFoundException e) {return false;}return true;}
}
  • 在自定义的类中加入,自己定义的注解,并指定类路径测试
@Configuration
public class UserConfig {@Bean@ConditionOnClass("org.springframework.data.redis.core.RedisTemplate")public User user(){return new User();}
}

image-20230220235828156

通过上面两个案例其实就解释了我们的 SpringBoot 是如何自动进行对象的选择装配的。

🪧 其实在我们的 SpringBoot 当中是提供了很多的 Condition 这样类似的注解的

我们可以到 spring-boot-autconfig 该包查看。

image-20230221000400680

image-20230221000553533

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

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

相关文章

堆,堆构建,堆排序,PriorityQueue和TopN问题

零. 前言 堆作为一种重要的数据结构&#xff0c;在面笔试中经常出现&#xff0c;排序问题中&#xff0c;堆排序作为一种重要的排序算法经常被问道&#xff0c;大顶堆小顶堆的应用经常出现&#xff0c;经典的问题TopN问题也是堆的重要应用&#xff0c;因此&#xff0c;了解并掌握…

Mac - Spotlight(聚焦)

文章目录一、Mac 中 Spotlight 的使用1、调用/打开 Spotlight2、执行搜索3、Spotlight 设置二、Mac 上的 Spotlight 开发1、关于 Spotlight2、使用 NSMetadataQuery 搜索示例三、mds 和 fsevents四、命令行访问 Spotlight五、Core Spotlight Framework六、Spotlight 插件相关资…

CSS预处理器sass和less

文章目录CSS预处理器什么是CSS预处理器Sass和LESS背景介绍Sass背景介绍LESS的背景介绍Sass安装Sass下载Ruby安装文件安装Ruby安装Sass编译Sass命令行编译命令行编译配置选项四种编译排版演示nested 编译排版格式expanded 编译排版格式compact 编译排版格式compressed 编译排版格…

登录逻辑漏洞整理集合

目录一、任意用户注册1.未验证邮箱/手机号2、不安全验证邮箱/手机号3.批量注册4.个人信息伪造5.前端验证审核绕过6.用户名覆盖二、任意用户登录1、万能密码2、验证码、密码回显3、登录检测不安全三、任意账号重置1、重置账号名2、验证码3、MVC数据对象自动绑定4、Unicode字符处…

独立产品灵感周刊 DecoHack #048 - 优秀独立开发产品推荐

如果有关注我的 Twitter 的朋友应该看到了&#xff0c;我上周末研究了两天 AI 画图&#xff0c;现在用 Ai 做图太强了&#xff0c;上周又升级 Stable Diffusion 玩了一下&#xff0c;和我去年试的时候相比强大了好多&#xff0c;而且插件LoRA模型玩法都还在快速迭代&#xff0c…

强化学习DQN之俄罗斯方块

强化学习DQN之俄罗斯方块强化学习DQN之俄罗斯方块算法流程文件目录结构模型结构游戏环境训练代码测试代码结果展示强化学习DQN之俄罗斯方块 算法流程 本项目目的是训练一个基于深度强化学习的俄罗斯方块。具体来说&#xff0c;这个代码通过以下步骤实现训练&#xff1a; 首先…

车机开发【Android SystemUI 架构音量控制详解】

SystemUI介绍 SystemUI摘要 在Android系统中SystemUI是以应用的形式运行在Android系统当中&#xff0c;即编译SystemUI模块会生产APK文件&#xff0c;源代码路径在frameworks/base/packages/SystemUI/&#xff0c;安装路径system/priv-app/-SystemUI。 什么是SystemUI 在前…

使用带有 Moveit 的深度相机来避免碰撞

文章目录 什么是深度相机?如何将 Kinect 深度相机添加到您的环境中在 Rviz 中可视化深度相机数据在取放场景中使用深度相机将深度相机与您的 Moveit 设置一起使用有很多优势。机器人可以避免未知环境中的碰撞,甚至可以对周围的变化做出反应。然而,将深度相机连接到您的设置并…

FlinkSQL行级权限解决方案及源码

FlinkSQL的行级权限解决方案及源码&#xff0c;支持面向用户级别的行级数据访问控制&#xff0c;即特定用户只能访问授权过的行&#xff0c;隐藏未授权的行数据。此方案是实时领域Flink的解决方案&#xff0c;类似离线数仓Hive中Ranger Row-level Filter方案。 源码地址: https…

数据分片(mycat)

1. 数据分片概念&#xff1a; 1.1. 分库分表 什么是分库分表&#xff1a; 将存放在一台数据库服务器中的数据&#xff0c;按照特定方式&#xff08;指的是程序开发的算法&#xff09;进行拆分&#xff0c;分散存放到多台数据库服务器中&#xff0c;以达到分散单台服务器负载的…

第51篇-某彩网登录参数分析-webpack【2023-02-21】

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、网站分析一、前言 今天我们看一个webpack的网站 aHR0cHM6Ly8xMGNhaTUwMC5jYy9sb2dpbg==二、网站分析 首先…

网络协议(一)应用层(自定制协议、HTTP协议)

目录 应用层&#xff1a;负责应用程序之间的数据沟通 一、自定制协议&#xff08;私有协议&#xff09; 二、HTTP协议 1&#xff09;、请求行解析&#xff1a;GET /index.html HTTP/1.1 第一部分&#xff1a;请求方法&#xff1a;多种多样&#xff0c;描述不同的请求目的 …

大数据知识图谱项目——基于知识图谱的医疗知识问答系统(详细讲解及源码)

基于知识图谱的医疗知识问答系统 一、项目概述 本项目基于医疗方面知识的问答&#xff0c;通过搭建一个医疗领域知识图谱&#xff0c;并以该知识图谱完成自动问答与分析服务。本项目以neo4j作为存储&#xff0c;基于传统规则的方式完成了知识问答&#xff0c;并最终以关键词执…

Verilog 学习第五节(串口发送部分)

小梅哥串口部分学习part1 串口通信发送原理串口通信发送的Verilog设计与调试串口发送应用之发送数据串口发送应用之采用状态机实现多字节数据发送串口通信发送原理 1&#xff1a;串口通信模块设计的目的是用来发送数据的&#xff0c;因此需要有一个数据输入端口 2&#xff1a;…

Qt中修改界面类的类名时需要注意的几个修改点

有些时候因为一些原因&#xff0c;需要修改Qt中创建的界面类&#xff0c;需要特别注意几个修改点。 比如将test类修改为test2类 修改test.h名称为test2.h文件&#xff1b;修改test.cpp名称为test2.cpp文件&#xff1b;修改test.ui名称为test2.ui文件&#xff1b;修改pro文件中…

多层感知机的区间随机初始化方法

摘要&#xff1a; 训练是构建神经网络模型的一个关键环节&#xff0c;该过程对网络中的参数不断进行微调&#xff0c;优化模型在训练数据集上的损失函数。参数初始化是训练之前的一个重要步骤&#xff0c;决定了训练过程的起点&#xff0c;对模型训练的收敛速度和收敛结果有重要…

Java基础43 异常(Exception)

异常&#xff08;Exception&#xff09;Exception1.1 异常的概念1.2 异常体系图&#xff08;☆&#xff09;1.3 异常处理分类1.3.1 运行时异常&#xff08;☆&#xff09;1.3.2 编译时异常&#xff08;☆&#xff09;1.4 异常处理&#xff08;☆&#xff09;1.4.1 try-catch异常…

【Git】Git下载安装与使用(一)

目录 1. 前言 1.1 什么是Git 1.2 使用Git能做什么 2. Git概述 2.1 Git简介 2.2 Git下载与安装 3. Git代码托管服务 3.1 常用的Git代码托管服务 3.2 码云代码托管服务 1. 前言 1.1 什么是Git Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码…

Cookies与Session会话技术详解

引言:日常生活中&#xff0c;人和人之间沟通交流&#xff0c;涉及到一个词----会话&#xff0c;软件中一样存在会话&#xff0c;如&#xff1a;网购登录&#xff0c;访问公司OA系统也是不断的会话&#xff0c;软件中如何管理浏览器客户端和服务端之间会话过程中的会话数据呢&am…

盘点四种自动化测试模型实例及优缺点

一&#xff0c;线性测试 1.概念&#xff1a; 通过录制或编写对应应用程序的操作步骤产生的线性脚本。单纯的来模拟用户完整的操作场景。 &#xff08;操作&#xff0c;重复操作&#xff0c;数据&#xff09;都混合在一起。 2.优点&#xff1a; 每个脚本相对独立&#xff0…