带你拿捏SpringBoot自动装配的核心技术?模块装配(@EnableXXX注解+@Import)+ 条件装配(@ConditionalXXX)

文章目录

    • Profile
      • 激活指定配置文件
        • 主配置文件中指定激活的profile
        • 命令行激活
        • 设置虚拟机参数激活
      • profile控制不到的地方
    • Spring原生的条件装配注解@Conditional
      • @Conditional接口讲解
      • 案例讲解
    • Spring Boot封装的条件装配注解@ConditionalXXX
      • 自己实现ConditionalOnBean
      • SpringBoot 源码案例注解

SpringBoot自动装配系列文章
@EnableXXX注解+@Import轻松实现SpringBoot的模块装配
带你拿捏SpringBoot自动装配的核心技术?模块装配(@EnableXXX注解+@Import)+ 条件装配(@ConditionalXXX)
深入探究Spring Boot自动配置原理及SPI机制:实现灵活的插件化开发

在这里插入图片描述

之前我们这篇文章@EnableXXX注解+@Import轻松实现SpringBoot的模块装配 提到了 SpringBoot自动装配的核心技术就是模块装配 + 条件装配!!!

在这篇文章我们完整的学习了模块装配的核心使用方法,通过模块装配,咱可以通过一个注解,一次性导入指定场景中需要的组件和配置。那么只靠模块装配的内容,就可以把这些装配都考虑到位吗?

只要配置类中声明了 @Bean 注解的方法,那这个方法的返回值就一定会被注册到 IOC 容器成为一个 Bean 。

所以,有没有办法解决这个问题呢?当然是有,总共有两种方式:Profile和@ConditionalXXX

先来学习第一种方式:Profile

Profile

通过Profile可以实现一套代码在不同环境启用不同的配置和功能。

@Profile 注解可以标注在组件上,当一个配置属性(并不是文件)激活时,它才会起作用,而激活这个属性的方式有很多种(启动参数、环境变量、web.xml 配置等)。

profile 提供了一种可以理解成“基于环境的配置”:根据当前项目的运行时环境不同,可以动态的注册当前运行环境匹配的组件

例如,我们分别定义开发、测试和生产这3个环境:

  • dev
  • test
  • production

创建某个Bean时,Spring容器可以根据注解@Profile来决定是否创建。我们这里还是拿上一章节的例子导入配置类中的LogBeanConfiguration

@Configuration
public class LogBeanConfiguration {@Bean@Profile("!dev")public MyLog myLog() {return new MyLog();}@Beanpublic LogUtil logUtil() {return new LogUtil();}}

如果当前的Profile设置为testproduction,则Spring容器才会调用myLog()创建MyLog类,而如果是dev环境,则这个类不会被创建

激活指定配置文件

主配置文件中指定激活的profile
spring:profiles:active: dev # 指定激活哪个配置文件
命令行激活

在linux生产环境中直接使用命令行启动项目,启动的同时可以指定激活的profile:

java -jar --spring.profiles.active=dev my-spring-boot-app.1.0.0.jar
设置虚拟机参数激活

在运行程序时,加上JVM参数-Dspring.profiles.active=test就可以指定以test环境启动。

命令行指定的方式和虚拟机参数设置的方式指定,都可以在IDEA的运行设置中进行配置,如下图:

在这里插入图片描述

profile控制不到的地方

使用Profile能根据不同的Profile进行条件装配,但是Profile控制比较糙, profile 控制的是整个项目的运行环境,无法根据单个 Bean 的因素决定是否装配。也是因为这个问题,出现了第二种条件装配的方式:@Conditional 注解

Spring原生的条件装配注解@Conditional

@Conditional接口讲解

要使用@Conditional注解,必须先了解一下Conditiona接口,它与@Conditional注解配合使用,通过源码我们也可以看出,使用@Conditional注解必须要指定实现Conditiona接口的class。

  • @Conditional 注解可以指定匹配条件,而被 @Conditional 注解标注的 组件类 / 配置类 / 组件工厂方法 必须满足 @Conditional 中指定的所有条件,才会被创建 / 解析。
  • @Conditional 是在 SpringFramework 4.0 版本正式推出的,它可以让 Bean 的装载基于一些指定的条件,换句话说,被标注 @Conditional 注解的 Bean 要注册到 IOC 容器时,必须全部满足 @Conditional 上指定的所有条件才可以。
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {/*** All {@link Condition Conditions} that must {@linkplain Condition#matches match}* in order for the component to be registered.*/Class<? extends Condition>[] value();
}

Conditiona接口中,只定义了一个方法matches,spring在注册组件时,也正是根据此方法的返回值TRUE/FALSE来决定是否将组件注册到spring容器中

@FunctionalInterface
public interface Condition {/*** Determine if the condition matches.* @param context 条件判断的上下文环境* @param metadata 正在检查的类或方法的注解元数据*/boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

matches中我们可以获取到ConditionContext接口,根据此接口对象可以获取BeanDefinitionRegistryConfigurableListableBeanFactory等重要对象信息,根据这些对象就可以获取和检查spring容器初始化时所包含的所有信息,再结合业务需求,就可以实现组件注册时的自定义条件判断。

案例讲解

LogBeanConfiguration 我们将之前的@Profile("!dev") 改为@Conditional(OnMyLogCondition.class)

@Configuration
public class LogBeanConfiguration {@Bean@Conditional(OnMyLogCondition.class)public MyLog myLog() {return new MyLog();}@Beanpublic LogUtil logUtil() {return new LogUtil();}}

OnMyLogCondition里面从ConditionContext获取ConfigurableListableBeanFactory,从而去判断需要有LogUtil Bean定义信息才会去创建MyLog类

public class OnMyLogCondition implements Condition {public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return context.getBeanFactory().containsBeanDefinition(LogUtil.class.getName());}
}

Spring Boot封装的条件装配注解@ConditionalXXX

Spring本身提供了条件装配@Conditional,但是要自己编写比较复杂的Condition来做判断,比较麻烦。Spring Boot则为我们准备好了几个非常有用的条件:

  • @ConditionalOnProperty:如果有指定的配置,条件生效;
  • @ConditionalOnBean:如果有指定的Bean,条件生效;
  • @ConditionalOnMissingBean:如果没有指定的Bean,条件生效;
  • @ConditionalOnMissingClass:如果没有指定的Class,条件生效;
  • @ConditionalOnWebApplication:在Web环境中条件生效;
  • @ConditionalOnExpression:根据表达式判断条件是否生效。

我们以比较常用的@ConditionalOnBean为例,之前@Conditional(OnMyLogCondition.class)是使用@Conditional 直接传入Condition接口的实现类进行判断是否要创建MyLog,现在使用@ConditionalOnBean 可以直接传入LogUtil.class ,它会帮我们实现这个判断!

@Configuration
public class LogBeanConfiguration {@Bean@ConditionalOnBean(LogUtil.class)//@Conditional(OnMyLogCondition.class)  原来的是传入自定义Conditional实现类public MyLog myLog() {return new MyLog();}@Beanpublic LogUtil logUtil() {return new LogUtil();}}

自己实现ConditionalOnBean

以刚刚的ConditionalOnBean为例,我们自己动手造轮子实现一下,这个注解,看下他是怎么实现的呢!

首先自定义注解ConditionalOnBean,定义有默认的Class 数组类型的value以及String数组类型的beanNames

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(OnMyBeanCondition.class)
public @interface ConditionalOnBean {Class<?>[] value() default {};String[] beanNames() default {};
}

接着就是实现这个OnMyBeanCondition类

public class OnMyBeanCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnBean.class.getName());// 匹配类型Class<?>[] classes = (Class<?>[]) attributes.get("value");for (Class<?> clazz : classes) {if (!context.getBeanFactory().containsBeanDefinition(clazz.getName())) {return false;}}// 匹配beanNameString[] beanNames = (String[]) attributes.get("beanNames");for (String beanName : beanNames) {if (!context.getBeanFactory().containsBeanDefinition(beanName)) {return false;}}return true;}
}

使用的时候就只需要传入对应的.class即可,原来的是直接传入Condition接口的实现类,现在这个ConditionalOnBean注解相当于封装了一层

@Configuration
public class LogBeanConfiguration {@Bean@ConditionalOnBean(LogUtil.class)//@Conditional(OnMyLogCondition.class)  原来的是传入自定义Conditional实现类public MyLog myLog() {return new MyLog();}@Beanpublic LogUtil logUtil() {return new LogUtil();}}

发现是不是其实springboot帮我们做的东西也不难,只是封装套了一层

SpringBoot 源码案例注解

@ConditionalOnBean({DataSource.class})
@ConditionalOnClass({JpaRepository.class})
@ConditionalOnMissingBean({JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class})
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories",name = {"enabled"},havingValue = "true",matchIfMissing = true
)
public class JpaRepositoriesAutoConfiguration {public JpaRepositoriesAutoConfiguration() {}
}

这段代码是一个基于Spring框架的Java代码片段,它定义了一个名为JpaRepositoriesAutoConfiguration的类,并使用了多个条件注解来控制这个类的自动配置。

@ConditionalOnBean({DataSource.class})注解表示只有在Spring容器中存在DataSource的Bean时,才会启用这个自动配置类。DataSource通常用于配置数据库连接。

@ConditionalOnClass({JpaRepository.class})注解表示只有在类路径中存在JpaRepository类时,才会启用这个自动配置类。JpaRepository是Spring Data JPA提供的接口,用于简化数据库访问的操作。

@ConditionalOnMissingBean({JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class})注解表示只有在容器中不存在JpaRepositoryFactoryBeanJpaRepositoryConfigExtension这两个Bean时,才会启用这个自动配置类。

@ConditionalOnProperty注解表示只有当指定的属性满足特定条件时,才会启用这个自动配置类。在这里,属性spring.data.jpa.repositories.enabled的值必须为true,或者如果该属性不存在时,也会启用这个自动配置类。

总结起来,这段代码定义了一个自动配置类JpaRepositoriesAutoConfiguration,它会根据一系列条件来判断是否要应用该自动配置。这些条件包括是否存在DataSource的Bean、是否存在JpaRepository类、是否缺少JpaRepositoryFactoryBeanJpaRepositoryConfigExtension这两个Bean,以及是否满足指定的属性条件。根据条件的不同,这个自动配置类可能会在Spring容器中自动配置一些与JPA相关的Bean。

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

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

相关文章

最新消息:OpenAI GPT Store 正式上线,GPTs 应用商店来了

OpenAI推出的两款新产品和服务&#xff1a;GPT Store和ChatGPT Team&#xff0c;提供了许多全新的解决方案和功能&#xff0c;旨在帮助用户更轻松地使用和构建GPT工具&#xff0c;同时也增加了公司的收入来源。GPT Store是一个全新的在线平台&#xff0c;允许用户创建和分享自定…

MyBatis实战指南(二):工作原理与基础使用详解

MyBatis是一个优秀的持久层框架&#xff0c;它支持定制化SQL、存储过程以及高级映射。那么&#xff0c;它是如何工作的呢&#xff1f;又如何进行基础的使用呢&#xff1f;本文将带你了解MyBatis的工作原理及基础使用。 一、MyBatis的工作原理 1.1 MyBatis的工作原理 工作原理…

论文阅读1---OpenCalib论文阅读之factory calibration模块

前言 该论文的标定间比较高端&#xff0c;一旦四轮定位后&#xff0c;可确定标定板与车辆姿态。以下为本人理解&#xff0c;仅供参考。 工厂标定&#xff0c;可理解为车辆相关的标定&#xff0c;不涉及传感器间标定 该标定工具不依赖opencv&#xff1b;产线长度一般2.5米 Fa…

Python基础知识:整理9 文件的相关操作

1 文件的打开 # open() 函数打开文件 # open(name, mode, encoding) """name: 文件名&#xff08;可以包含文件所在的具体路径&#xff09;mode: 文件打开模式encoding: 可选参数&#xff0c;表示读取文件的编码格式 """ 2 文件的读取 文…

Find My资讯|AirTag 2或推迟上市,Find My功能十分强大

苹果于 2021 年4月推出了初代 AirTag。苹果已将第二代 AirTag 的推出推迟到 2025 年&#xff0c;目前苹果官方并不急于推出AirTag 2的原因还有AirTag所搭载的搜寻定位功能非常的强大&#xff0c;在市场上几乎没有任何竞争对手可言。 AirTag使用蓝牙和苹果设备的“查找我的”网…

Redis命令 - Lists命令组常用命令

先创建一个 key 叫做 mylist&#xff0c;mylist存一个list。 list数据类型底层是一个链表。先进后出&#xff0c;后进先出。 命令中的L&#xff08;Left&#xff09;、R&#xff08;Right&#xff09;代表链表的头部L&#xff08;下标0的位置&#xff09;和尾部R&#xff08;…

uni-app分包预下载

模块的二级页面&#xff0c;按模块处理成分包页面&#xff0c;有以下好处&#xff1a; 按模块管理页面&#xff0c;方便项目维护。减少主包体积&#xff0c;用到的时候再加载分包&#xff0c;属于性能优化解决方案。 ::: tip 温馨提示 通过 VS Code 插件 uni-create-view 可…

AI与编程学习

在C语言中&#xff0c;指针通常与字符数组或字符串打交道时会涉及到ASCII码的转换&#xff0c;而不是用于表现多位数的第一位。48这个值对应的是ASCII码表中数字字符0的编码。 如果你有一个表示多位数的字符数组&#xff0c;例如&#xff1a; c char number[] "1234&qu…

TortoiseSVN·文件锁定与清理

安装 TortoiseSVN 的时候&#xff0c;选择 svn 命令可用, 选择 will be intalled on local hard drive 。 在锁定的文件夹内 cmd 进入终端&#xff0c;输入 find . -type f -name ".svn/lock" -exec rm -f {} \; 删除所有锁定文件。进行清理操作&#xff1a;svn clea…

Apache ActiveMQ RCE CNVD-2023-69477 CVE-2023-46604

漏洞简介 Apache ActiveMQ官方发布新版本&#xff0c;修复了一个远程代码执行漏洞&#xff0c;攻击者可构造恶意请求通过Apache ActiveMQ的61616端口发送恶意数据导致远程代码执行&#xff0c;从而完全控制Apache ActiveMQ服务器。 影响版本 Apache ActiveMQ 5.18.0 before 5.1…

LeetCode刷题.15(哈希表与计数排序解决41. 缺失的第一个正数)

给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1a;nums …

使用setdefault撰写文本索引脚本(出自Fluent Python案例)

背景介绍 由于我们主要介绍撰写脚本的方法&#xff0c;所以用一个简单的文本例子进行分析 a[(19,18),(20,53)] Although[(11,1),(16,1),(18,1)] ambiguity[(14,16)] 以上内容可以保存在一个txt文件中&#xff0c;任务是统计文件中每一个词&#xff08;包括字母&#xff0c;数…

wireshark使用教程

目录 windows平台安装Wireshark组件选择Additional TasksPacket CaptureUSB CaptureNpcap Installation Options Ubuntu上安装 Wireshark不使用 sudo 运行 Wireshark 使用GUI抓包使用命令行抓包确定抓取哪个网卡的报文抓取数据包停止抓包设置过滤条件 参考资料 Wireshark 是一款…

系列七、Spring Security中基于Jdbc的用户认证 授权

一、Spring Security中基于Jdbc的用户认证 & 授权 1.1、概述 前面的系列文章介绍了基于内存定义用户的方式&#xff0c;其实Spring Security中还提供了基于Jdbc的用户认证 & 授权&#xff0c;再说基于Jdbc的用户认证 & 授权之前&#xff0c;不得不说一下Spring Se…

DM数据库安装注意事项

数据库安装注意事项 一、安装前 一些参数需要在数据库创建实例前找用户确认。 参数名参数掩码参数值备注数据页大小PAGE_SIZE32数据文件使用的页大小(缺省使用8K&#xff0c;建议默认&#xff1a;32)&#xff0c;可以为 4K、8K、16K 或 32K 之一&#xff0c;选择的页大小越大…

1.5矩阵元素的引用

通过下标来引用矩阵的元素 A(3, 2)表示A矩阵第3行第2列的元素。 >> arr [1,2,3;4,5,6]; >> arr(4, 5) 10arr 1 2 3 0 04 5 6 0 00 0 0 0 00 0 0 0 10>> 如果引用元素超过矩阵的大小将自…

React项目实战--------极客园项目PC端

项目介绍&#xff1a;主要将学习到的项目内容进行总结&#xff08;有需要项目源码的可以私信我&#xff09; 关于我的项目的配置如下&#xff0c;请注意下载的每个版本不一样&#xff0c;写的api也不一样 一、项目介绍 1.资料 1&#xff09;短信接收&M端演示&#xff1a…

SpringFramework实战指南(二)

SpringFramework实战指南&#xff08;二&#xff09; 2.1 Spring 和 SpringFramework概念2.2 SpringFramework主要功能模块2.3 SpringFramework 主要优势 2.1 Spring 和 SpringFramework概念 Spring-ioc 广义的 Spring&#xff1a;Spring 技术栈&#xff08;全家桶&#xff0…

学会编写自定义configure脚本,轻松实现定制化配置

学会编写自定义configure脚本&#xff0c;轻松实现定制化配置 一、configure脚本的作用和重要性二、configure脚本的基本结构和语法三、编写自定义configure脚本的步骤四、示例五、常见的问题总结 一、configure脚本的作用和重要性 configure脚本是用于自动配置软件源代码的脚…

周赛379(排序、分类讨论、记忆化搜索(动态规划))

文章目录 周赛379[3000. 对角线最长的矩形的面积](https://leetcode.cn/problems/maximum-area-of-longest-diagonal-rectangle/)排序 [3001. 捕获黑皇后需要的最少移动次数](https://leetcode.cn/problems/minimum-moves-to-capture-the-queen/)分类讨论 [3002. 移除后集合的最…