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

news2024/12/23 23:25:40

【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 {
    @Bean
    public 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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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.coloradmin.cn/o/360792.html

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

相关文章

零基础小白如何学会数据分析?

随着数字经济、大数据时代的发展&#xff0c;数据已然成为当下时代最重要的盈利资源&#xff0c;让企业在做决策和计划方案时更有针对性和依据&#xff0c;能提前预测市场发展方向&#xff0c;做好布局。由此而产生的数据分析岗位也逐渐被更多企业重视&#xff0c;特别是中大型…

社招中级前端笔试面试题总结

HTTP世界全览 互联网上绝大部分资源都使用 HTTP 协议传输&#xff1b;浏览器是 HTTP 协议里的请求方&#xff0c;即 User Agent&#xff1b;服务器是 HTTP 协议里的应答方&#xff0c;常用的有 Apache 和 Nginx&#xff1b;CDN 位于浏览器和服务器之间&#xff0c;主要起到缓存…

Notion AI是什么?和chatgpt比哪个好?

最近对于人工智能的热度可谓是前所未有的高涨&#xff0c;毕竟现在的人工智能发展是越来越快&#xff0c;能做的事情也是越来越多&#xff0c;不再是那种低等的假智能小爱同学和siri那种。今天我们主要来聊聊Notion AI和chatgpt吧&#xff0c;Notion AI是什么&#xff1f;和cha…

leaflet 删除所有的marker图层,保留其他图层(085)

第085个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet项目中清除所有的marker图层,保留其他图层,详情请参考源代码。这里面主要用到了(layer instanceof L.Marker ,注意 L.Marker中Marker首字母要大写。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行…

【5000左右电脑配置清单】预算不高于5000,不带显示器的电脑配置清单推荐

由于本人是学生党经济有限&#xff0c;预算不高于5000元配一套电脑主机&#xff0c;说实话5000左右的电脑配置已经很好了&#xff0c;今天站长整理了几款配置给大家参考参考&#xff0c;更多电脑配置还请继续关注西安SEO优化站点&#xff01; 配置1&#xff1a; <CPU> I5…

Kafka 介绍和使用

文章目录前言1、Kafka 系统架构1.1、Producer 生产者1.2、Consumer 消费者1.3、Consumer Group 消费者群组1.4、Topic 主题1.5、Partition 分区1.6、Log 日志存储1.7、Broker 服务器1.8、Offset 偏移量1.9、Replication 副本1.10、Zookeeper2、Kafka 环境搭建2.1、下载 Kafka2.…

Django使用jinja2模板

Django使用jinja2模板 jinja2介绍 Jinja2&#xff1a;是 Python 下一个被广泛应用的模板引擎&#xff0c;是由Python实现的模板语言&#xff0c;他的设计思想来源于 Django 的模板引擎&#xff0c;并扩展了其语法和一系列强大的功能&#xff0c;尤其是Flask框架内置的模板语言…

易基因|RRBS单碱基绘制580种动物的基因组规模DNA甲基化谱:Nature子刊

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。2023年01月16日&#xff0c;奥地利科学院分子医学研究中心(CeMM)研究团队在《Nat Commun》杂志发表了题为“Comparative analysis of genome-scale, base-resolution DNA methylation prof…

带你彻底了解浮点型数据的存储

&#x1f680;&#x1f680;&#x1f680;大家觉不错的话&#xff0c;就恳求大家点点关注&#xff0c;点点小爱心&#xff0c;指点指点&#x1f680;&#x1f680;&#x1f680; 目录 &#x1f430;浮点型在内存的存储 &#x1f914;提示&#xff1a;数据类型的存储范围 &a…

定位于企业数字化底座,开箱可用(spring cloud+Vue)基础框架,赶紧收藏!

项目介绍&#xff1a;JVS是什么&#xff1f;JVS是企业级应用构建的基础脚手架&#xff0c;提供开箱即用的基础功能集成&#xff0c;其中集成了账户管理、租户管理、用户权限体系、三方登录、环境配置、各种业务日志等功能&#xff0c;还提供了对接低代码、数据中台的能力。JVS能…

vs code 远程连接服务器并debug (python)

文章目录1. 正常官网下载vs code2. 使用remote SSH extension3. 连接远程服务器 &#xff08;launch.json&#xff09;launch.json、setting.json、task.json4、给调试传参数查看中间变量 VARIABLES1. 正常官网下载vs code 2. 使用remote SSH extension 3. 连接远程服务器 &am…

【Fastdfs】| 入门连续剧——安装

作者&#xff1a;狮子也疯狂 专栏&#xff1a;《spring开发》 坚持做好每一步&#xff0c;幸运之神自然会降临在你的身上 目录一. &#x1f981; 前言Ⅰ. &#x1f407; 为什么要使用分布式文件系统&#xff1f;1.1 单机系统 vs 独立文件服务器1.2 分布式文件系统1.3 FastDFS引…

06- OpenCV查找图像轮廓 (OpenCV基础) (机器视觉)

知识重点 灰度图转换: gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)二值化: 返回两个东西&#xff0c;一个阈值&#xff0c; 一个是二值化的图: thresh, binary cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)查找轮廓: 返回两个结果&#xff0c;分别是轮廓和层级: c…

微软应用商店错误代码0x80072EFD怎么办?(已解决)

有用户在使用Win11电脑系统的时候&#xff0c;想要在应用商店里面去进行软件的下载。但是在下载的过程中&#xff0c;开启应用商店却无法进行软件下载&#xff0c;出现了错误代码0x80072EFD。那么这个问题怎么去进行解决呢&#xff1f;一起来看看以下的解决方法分享吧。 微软应…

基于图数据库 NebulaGraph 实现的欺诈检测方案及代码示例

本文是一个基于 NebulaGraph 图算法、图数据库、机器学习、GNN 的 Fraud Detection 方法综述。在阅读本文了解欺诈检测的基本实现方法之余&#xff0c;也可以在我给大家准备的 Playground 上跑下数据。 下面进入本次图数据库的欺诈检测实践&#xff1a; 建立反欺诈图谱 欺诈…

洛谷P5735 【深基7.例1】距离函数 C语言/C++

【深基7.例1】距离函数 题目描述 给出平面坐标上不在一条直线上三个点坐标 (x1,y1),(x2,y2),(x3,y3)(x_1,y_1),(x_2,y_2),(x_3,y_3)(x1​,y1​),(x2​,y2​),(x3​,y3​)&#xff0c;坐标值是实数&#xff0c;且绝对值不超过 100.00&#xff0c;求围成的三角形周长。保留两位…

C/C++每日一练(20230221)

目录 1. 格雷编码 2. 矩阵问题 3. 搜索旋转排序数组 II 1. 格雷编码 格雷编码是一个二进制数字系统&#xff0c;在该系统中&#xff0c;两个连续的数值仅有一个位数的差异。 给定一个代表编码总位数的非负整数 n&#xff0c;打印其格雷编码序列。即使有多个不同答案&#…

浅析跨境电商行业为何发展如此迅猛?

跨境电商这几年成长的突飞猛进&#xff0c;在我国民众内的知名度堪比国内电商。这里对两者进行一个简单的介绍&#xff0c;国内电商是指消费者在电商平台上挑选商品&#xff0c;然后跟商家下单喜欢的商品&#xff0c;商家进行发货&#xff0c;消费者通过快递得到商品&#xff0…

Java流程控制

目录 前言 一、用户交互Scanner及其进阶使用 输入的数据为字符串类型 输入的数据为整型或者浮点型 练习 二、顺序结构 三、选择结构 if单选泽结构 if双选择结构 if多选择结构 嵌套的if结构 switch多选择结构 四、循环结构 while循环 Do...while循环 For循环 练习 利用for循环…

2.20 crm day01 配置路由router less使用 axios二次封装

需求&#xff1a; 目录 1.配置路由 2.less使用 vue2使用以下版本 3.axios二次封装 1.配置路由 1.1.1 官方链接&#xff1a;安装 | Vue Router npm i vue-router3.6.5 注意&#xff1a;vue2项目不能用vue-router四版本以上 1.2.1.创建router/index.js 在该文件中 //1.引…