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

news2025/2/25 13:47:45

文章目录

    • 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();
    }
    
    @Bean
    public 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();
    }
    
    @Bean
    public 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();
    }
    
    @Bean
    public 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 {

    @Override
    public 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;
            }
        }
        // 匹配beanName
        String[] 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();
    }
    
    @Bean
    public 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.coloradmin.cn/o/1383186.html

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

相关文章

【目标检测】评价指标:mAP概念及其计算方法(yolo源码/pycocotools)

本篇文章首先介绍目标检测任务中的关键评价指标mAP的概念&#xff1b;然后介绍其在yolo源码和pycocotools工具中的实现方法&#xff1b;最后比较两种mAP的计算方法的不同之处。 目标检测中的评价指标&#xff1a; mAP概念及其计算方法(yolo源码/pycocotools) 混淆矩阵概念及其…

6.2 声音编辑工具GoldWave5简介(7)

6.2.5其它常用功能 1&#xff0e;高低通 把录制的语音和背景音乐融合在一起时&#xff0c;可能会出现背景音乐音量过大&#xff0c;语音音量过小的现象&#xff0c;这时可以选择“低通”将背景音乐的音量降低一些。 (1)选择【效果】|【波波器】|【低通/高通】命令&#xff0…

开箱即用的企业级前后端分离【.NET Core6.0 Api + Vue 2.x + RBAC】权限框架-Blog.Core

前言 今天要给大家推荐一个开箱即用的企业级前后端分离【.NET Core6.0 Api Vue 2.x RBAC】权限框架&#xff08;提高生产效率&#xff0c;快速开发就选它&#xff09;&#xff1a;Blog.Core。 推荐原因 Blog.Core通过详细的文章和视频讲解&#xff0c;将知识点各个击破&…

2024-1-12 关于SVPWM的理解疑问

直流母线电压利用率是指逆变电路&#xff08;电机控制器&#xff09;所能输出的交流电压基波最大幅值U1m和直流母线电压之比。 电压利用率 SVPWM算法理解&#xff08;二&#xff09;——关于非零基本矢量幅值和线电压幅值的解释 因此我们在实际应用中提供的直流侧电压Udc&…

ssm基于Web的课堂管理系统设计与实现论文

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…

【Linux】 系统目录结构

进入到根目录 cd /ls目录名具体作用/存放系统系统相关的目录文件/boot放置linux系统内核文件和启动时用到的一些引导文件/home包含linux系统上各用户的主目录&#xff0c;子目录名称默认以该用户名命名/root系统管理员root的家目录/bin包含常用的命令文件&#xff08;如ls 等&a…

STM32 CAN学习(一)

STM32 CAN CAN协议简介 CAN是控制器局域网络(Controller Area Network)的简称&#xff0c;它是由研发和生产汽车电子产品著称的德国BOSCH公司开发的&#xff0c;并最终成为国际标准&#xff08;ISO11519&#xff09;&#xff0c;是国际上应用最广泛的现场总线之一。CAN总线协…

Qat++,轻量级开源C++ Web框架

目录 一.简介 二.编译Oat 1.环境 2.编译/安装 三.试用 1.创建一个 CMake 项目 2.自定义客户端请求响应 3.将请求Router到服务器 4.用浏览器验证 一.简介 Oat是一个面向C的现代Web框架 官网地址&#xff1a;https://oatpp.io github地址&#xff1a;https://github.co…

JavaScript中alter、confrim、prompt的区别及使用

文章目录 一、alter1.什么是alert&#xff1f;2.alter的使用 二、confrim1.什么是confrim&#xff1f;2.confrim的使用 三、prompt1.什么是prompt&#xff1f;2.prompt的使用 四、总结alter、confrim、prompt的区别 一、alter 1.什么是alert&#xff1f; 在JavaScript中&…

ssm基于Vue的健身房会员管理系统+vue论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#x…

强化学习应用(四):基于Q-learning算法的无人车配送路径规划(提供Python代码)

一、Q-learning算法介绍 Q-learning是一种强化学习算法&#xff0c;用于解决基于环境的决策问题。它通过学习一个Q-table来指导智能体在不同状态下采取最优动作。下面是Q-learning算法的基本步骤&#xff1a; 1. 定义环境&#xff1a;确定问题的状态和动作空间&#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>> 如果引用元素超过矩阵的大小将自…

ssm基于spring和vue开发的web新闻流媒体平台论文

摘 要 如今的时代&#xff0c;是有史以来最好的时代&#xff0c;随着计算机的发展到现在的移动终端的发展&#xff0c;国内目前信息技术已经在世界上遥遥领先&#xff0c;让人们感觉到处于信息大爆炸的社会。信息时代的信息处理肯定不能用之前的手工处理这样的解决方法&#x…

隧道应用3-Cobalt Strike正反向连接多层内网

Cobalt Strike 正向连接多层内网&#xff1a; teamserver 不允许访问 B &#xff0c;但是服务器上A有权限&#xff08; A 与 B 在同一网段&#xff09;&#xff0c;若 A 服务上已经有了 cs 的后门&#xff0c;则可以通过 cs 的正向连接去连接 B &#xff0c;在 teamserver 通…

基于机器学习的高考志愿高校及专业分析系统

本项目在“基于 Python 的高考志愿高校及专业分析系统”基础上补充添加了机器学习算法对高考总问进行预测&#xff1b; 项目采用了网络爬虫技术&#xff0c;从指定的高考信息网站上抓取了各大高校的历年录取分数线数据。 通过精细的数据清洗过程&#xff0c;这些数据被存储于…

黑马程序员SpringBoot2-开发实用篇

视频连接&#xff1a;开发实用篇-67-手工启动热部署_哔哩哔哩_bilibili 热部署 手动启动热部署 热部署仅包含restart的过程。 自动启动热部署 按CtrlAltShift/打开下列界面。 禁用热部署 配置高级 ConfigurationProperties 宽松绑定/松散绑定 常用计量单位绑定 数据校验 设置…

阿里云优惠券介绍、种类、领取入口及使用教程

阿里云优惠券是阿里云提供的一种优惠活动&#xff0c;旨在帮助用户节省购买云服务产品的费用。本文将为大家详细介绍阿里云优惠券的相关信息&#xff0c;包括优惠券的介绍、种类、领取入口以及使用教程。 一、阿里云优惠券介绍 阿里云优惠券是阿里云提供给用户的一种优惠凭证&…

超强站群系统v9.0:最新蜘蛛池优化技术,一键安装,内容无缓存刷新,高效安全

安全、高效&#xff0c;化的优化利用php性能&#xff0c;使得运行流畅稳定 独创内容无缓存刷新不变&#xff0c;节省硬盘。防止搜索引擎识别蜘蛛池 蜘蛛池算法&#xff0c;轻松构建站点&#xff08;电影、资讯、图片、论坛等等&#xff09; 可以个性化每个网站的风格、内容、…

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…

2.1 常用计算机网络体系结构

2.1 常用计算机网络体系结构 2.1.1 OSI体系结构 1、为了使不同体系结构的计算机网络都能够互联&#xff0c;国际标准化组织于1977年成立了专门机构研究该问题&#xff0c;不久他们就提出了一个试图使各种计算机在世界范围内都能够互连成网的标准框架&#xff0c;也就是著名的…