【Spring Boot】详解条件注解以及条件拓展注解@Conditional与@ConditionalOnXxx
Spring
@Conditional
Spring 4.0+提供的注解。作用是给需要装载的Bean增加一个条件判断。只有满足条件才会装在到IoC容器中。而这个条件可以由自己去完成的,可以通过重写Condition接口重写matches()方法去实现自定义的逻辑。所以说这个注解增加了对Bean装载的灵活性。
源码
可以看出来首先可以修饰在类、接口、枚举以及方法上。并且可以接收一个或多个实现Condition接口的类。

那么在Condition接口中只有一个返回布尔类型的matches()方法。从这个单词也看得出来这是匹配的意思,所以就是匹配校验Bean是否可以被加载进IoC容器中。Determine if the condition matches(确定条件是否匹配)。

实战代码
以下先建两个Bean类、一个条件类、一个配置类、以及测试Main类。需要注意的是条件类中的参数并不是Spring的上下文ApplicationContext,所以其内容需要设置在-vm options中。至于这个-vm [options]中的options可以通过DOS窗口输入Java就可以看到有什么选项了。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Animal {private String name;private String sex;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {private String name;private Integer age;
}
public class PersonCondition implements Condition {/*** @param context 上下文* @param metadata 注解元信息*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 通过条件上下文获取环境中的配置文件信息String property = context.getEnvironment().getProperty("spring.createBean");if(null == property) {return false;}return property.contains("person");}
}
public class AnimalCondition implements Condition {/*** @param context 上下文* @param metadata 注解元信息*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 通过条件上下文获取环境中的配置文件信息String property = context.getEnvironment().getProperty("spring.createBean");if(null == property) {return false;}return property.contains("animal");}
}
public class ConditionalTest {public static void main(String[] args) {// 通过Spring上下文ApplicationContext传入配置类获取其中的Bean描述并输出ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConditionalConfig.class);Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println);}
}
测试结果

SpringBoot
关于@ConditionalOnXxx注解是在SpringBoot中拓展出来的,是原先Spring框架中没有存在的注解。那么以下就逐一去了解每个注解的作用。需要说的是这些注解全部都可以注解在类、接口、枚举和方法上。

从上图可以发现有十三种是@ConditionalOnXxx。其中就不了解@ConditionalOnCloudPlatform与@ConditionOnJndi这两个注解了。
上面的扩展注解我们可以简单的分为以下几类:
- Bean作为条件:@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnSingleCandidate。
- 类作为条件:@ConditionalOnClass、@ConditionalOnMissingClass。
- SpEL表达式作为条件:@ConditionalOnExpression。
- Java版本作为条件: @ConditionalOnJava
- 配置属性作为条件:@ConditionalOnProperty。
- 资源文件作为条件:@ConditionalOnResource。
- 是否Web应用作为判断条件:@ConditionalOnWebApplication、@ConditionalOnNotWebApplication。
条件为Bean的情况
@ConditionalOnBean
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnBean {/*** 需要作为条件的类的Class对象数组*/Class<?>[] value() default {};/*** 需要作为条件的类的Name, Class.getName()*/String[] type() default {};/*** (用于指定注解修饰的Bean)条件所需的注解类*/Class<? extends Annotation>[] annotation() default {};/*** Spring容器中Bean的名字*/String[] name() default {};/*** 搜索容器层级,当前容器,父容器*/SearchStrategy search() default SearchStrategy.ALL;/*** 可能在其泛型参数中包含指定Bean类型的其他类*/Class<?>[] parameterizedContainer() default {};
}
源码中的属性就不一一展示测试了,这里就测试value于name即可,value传入的是Class类型。而这个注解的含义很简单:如果IoC容器中存在该注解中value属性对应的Bean,那么就加载被该注解注解的Bean。否则不加载。测试代码采用上面Spring目录下的测试结果中的代码。这里主要展示配置类中的逻辑。
@Configuration
public class ConditionalConfig {@Beanpublic Person person() {return new Person();}@Bean@ConditionalOnBean(Person.class)//@ConditionalOnBean(name = "com.gok.entity.Person")public Animal animal() {return new Animal();}
}

这里需要注意的是,Spring加载Bean是在配置类中自上而下加载的,所以说如果person()与animal()两个方法换位置的话Animal是不会被加载到IoC容器中的,因为在它加载时Person还没被加载入IoC容器。
@ConditionalOnMissingBean
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {/*** 需要作为条件的类的Class对象数组*/Class<?>[] value() default {};/*** 需要作为条件的类的Name, Class.getName()*/String[] type() default {};/*** 匹配Bean的时候需要忽视的Class对象数组,一般是父类* @ConditionalOnMissingBean(value = JdbcFactory.class, ignored = MySqlDefaultFactory.class)*/Class<?>[] ignored() default {};/*** 匹配Bean的时候需要忽视的类的Name, Class.getName()*/String[] ignoredType() default {};/*** (用于指定注解修饰的Bean)条件所需的注解类*/Class<? extends Annotation>[] annotation() default {};/*** Spring容器中Bean的名字*/String[] name() default {};/*** 搜索容器层级,当前容器,父容器*/SearchStrategy search() default SearchStrategy.ALL;/*** 可能在其泛型参数中包含指定Bean类型的其他类*/Class<?>[] parameterizedContainer() default {};
}
理解了上面注解的作用,那这个注解就游刃有余了,miss单词意为错过、没有的意思。所以这个注解的作用就是:如果IoC容器中不存在该注解中value属性对应的Bean,那么就加载被该注解注解的Bean。否则不加载。
@Configuration
public class ConditionalConfig {@Beanpublic Person person() {return new Person();}@Bean@ConditionalOnMissingBean(Person.class)//@ConditionalOnMissingBean(name = "com.gok.entity.Person")public Animal animal() {return new Animal();}
}
@ConditionalOnSingleCandidate
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnSingleCandidate {/*** 需要作为条件的类的Class对象*/Class<?> value() default Object.class;/*** 需要作为条件的类的Name, Class.getName()*/String type() default "";/*** 搜索容器层级,当前容器,父容器*/SearchStrategy search() default SearchStrategy.ALL;
}
此注解从单词single与candidate可以得出是单个候选人的意思。大致可以猜测是存在相同类型的Bean的话只会对单个有效。我尝试将其放到person02()上,还是一样将这两个Bean加载到了IoC当中,但是放在第一个person01()上,导致person01没有被加载到IoC容器当中。所以此Bean的作用就是:如果当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean的时候则生效。即同类型的Bean中,首选Bean无法被加载入IoC容器中。
@Configuration
public class ConditionalConfig {@Bean@ConditionalOnSingleCandidatepublic Person person01() {return new Person();}@Beanpublic Person person02() {return new Person();}
}
条件为类的情况
@ConditionalOnClass
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {/*** 需要作为条件的类的Class对象数组*/Class<?>[] value() default {};/*** 需要作为条件的类的Name, Class.getName()*/String[] name() default {};
}
这个其实和@ConditionalOnBean类似,但是那个注解是在IoC容器中或者是类全限定名找是否存在该Spring Bean。而@ConditionalOnClas是在IoC容器中或者是类全限定名找到是否存在该类。如果存在就加载,不存在就不加载到IoC容器中。
@ConditionalMissingClass
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnMissingClass {/*** 需要作为条件的类的Name, Class.getName()*/String[] value() default {};
}
与@ConditionalOnClass相反。会在这里一起展示代码以及测试的结果。Plant类是真实存在的,所以说person01被加载到IoC容器中,而person02没有被加载到IoC当中。
@Configuration
public class ConditionalConfig {@Bean@ConditionalOnClass(Animal.class)public Person person01() {return new Person();}@Bean@ConditionalOnMissingClass("com.gok.entity.Animal")public Person person02() {return new Person();}
}

条件为SpEL表达式的情况
@ConditionalOnExpression
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnExpressionCondition.class)
public @interface ConditionalOnExpression {/*** 要作为条件的SpEL表达式*/String value() default "true";
}
这个注解就是用来判断该Bean是否符合SpEL表达式,至于什么是SpEL表达式就自行百度学习了,就不多放篇幅去详细说明了。这里我设置person01为true,而person02为false。
@Configuration
public class ConditionalConfig {@Bean@ConditionalOnExpression("true")public Person person01() {return new Person();}@Bean@ConditionalOnExpression("false")public Person person02() {return new Person();}
}
条件为Java的情况
@ConditionalOnJava
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnJavaCondition.class)
public @interface ConditionalOnJava {/*** 比较方式,Range.EQUAL_OR_NEWER:当前版本等于或高于、Range.OLDER_THAN:当前版本老于,越早的版本越老*/ConditionalOnJava.Range range() default ConditionalOnJava.Range.EQUAL_OR_NEWER;/*** 指定JAVA版本*/JavaVersion value();/*** Range options.*/public static enum Range {/*** Equal to, or newer than the specified {@link JavaVersion}.*/EQUAL_OR_NEWER,/*** Older than the specified {@link JavaVersion}.*/OLDER_THANprivate Range() {}}
}
此注解用来判断当前运行环境的Java版本是多少。符合范围内的条件才会加载Bean。
@Configuration
public class ConditionalConfig {@Bean@ConditionalOnJava(JavaVersion.EIGHT)public Person person01() {return new Person();}@Bean@ConditionalOnJava(JavaVersion.NINE)public Person person02() {return new Person();}
}


条件为配置条件的情况
@ConditionalOnProperty
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {/*** 对应property名称的值*/String[] value() default {};String[] name() default {};/*** property名称的前缀,可有可无*/String prefix() default "";/*** 与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置*/String havingValue() default "";/*** 缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错*/boolean matchIfMissing() default false;
}
此注解用于条件配置中读取peoperties文件中的信息。本人测试读取yml无效,需要在配置类上多添加个@PropertySource注解读取文件才能够使用配置条件注解。
# application.properties中的内容
com.gok.test=true
com.gok.password=123456
@Configuration
// 读取properties文件的方式 可以配合@Value注解读取详细信息
@PropertySource(value = "classpath:application.properties", encoding = "UTF-8")
//@PropertySources({@PropertySource(value = "classpath:application.properties", encoding = "UTF-8")})
public class ConditionalConfig {@Bean@ConditionalOnProperty("com.gok.test")public Person person01() {return new Person();}@Bean@ConditionalOnProperty(name = "com.gok.test")public Person person02() {return new Person();}@Bean@ConditionalOnProperty("com.gok.password")public Person person03() {return new Person();}@Bean@ConditionalOnProperty(name = "com.gok.password", havingValue = "123456")public Person person04() {return new Person();}@Bean@ConditionalOnProperty(name = "com.gok.password", havingValue = "123456789")public Person person05() {return new Person();}@Bean@ConditionalOnProperty(value = "com.gok.password=123456", matchIfMissing = true)public Person person06() {return new Person();}@Bean// 这里要注意如果要使用prefix前缀的话 必须带上name或者value// 或者会报错:The name or value attribute of @ConditionalOnProperty must be specified// 以下拼接即为:是否存在com.gok.password这个属性@ConditionalOnProperty(prefix = "com.gok", name = "password")public Person person07() {return new Person();}
}
条件为资源条件的情况
@ConditionalOnResource
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnResourceCondition.class)
public @interface ConditionalOnResource {/*** 要作为判断条件的资源文件名称 @ConditionalOnResource(resources = ”mybatis.xml”)*/String[] resources() default {};
}
查询指定的资源,不仅仅可以查找classpath下的文件,还可以用来查找外部资源是否存在。
@Configuration
public class ConditionalConfig {@Bean@ConditionalOnResource(resources = "https://www.baidu.com")public Person person01() {return new Person();}@Bean@ConditionalOnResource(resources = "classpath:application.properties")public Person person02() {return new Person();}@Bean@ConditionalOnResource(resources = "https://www.baiduhaha.com")public Person person03() {return new Person();}
}
条件为Web应用的情况
@ConditionalOnWebApplication
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {/*** 需要作为条件的Web应用程序的必需类型*/ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY;/*** Available application types.*/public static enum Type {/*** 任何web应用都将匹配*/ANY,/*** 仅基于servlet的Web应用程序将匹配*/SERVLET,/*** 仅基于反应式的Web应用程序将匹配*/REACTIVE;private Type() {}}
}
判断当前是否为Web项目/Web环境。主要就是从是否有导入Web的依赖。这里简单介绍以下三种不同情况的依赖引入情况。
<!-- 无Web容器 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 使用Tomcat/Servlet Web容器 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 使用Netty 响应式的Web容器 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@ConditionalOnNotWebApplication
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnNotWebApplication {
}
参考文章
https://www.cnblogs.com/dusucyy/p/16609736.html
@ConditionalOnBean详解_你就像甜甜的益达的博客-CSDN博客a
相关文章:
【Spring Boot】详解条件注解以及条件拓展注解@Conditional与@ConditionalOnXxx
Spring Conditional Spring 4.0提供的注解。作用是给需要装载的Bean增加一个条件判断。只有满足条件才会装在到IoC容器中。而这个条件可以由自己去完成的,可以通过重写Condition接口重写matches()方法去实现自定义的逻辑。所以说这个注解增加了对Bean装载的灵活性。…...
Android 12 源码分析 —— 应用层 一(SystemUI准备篇)
Android 12 源码分析 —— 应用层一(SystemUI准备篇) 在接下来的时间中,将会使用Pixel 3(blueline)作为研究对象,选用AOSP的android-12.0.0_r34分支作源代码。 先从android的应用层进行探析,然后慢慢深入android的fr…...
记录 MySQL 如何开启已有的定时任务
1.首先,确保你已经在MySQL的配置文件my.ini中启用了事件调度器。在[mysqld]部分添加event_schedulerON,然后保存文件并重启MySQL服务。这将启用MySQL的事件调度器功能。 但如果是线上业务不能停也可以在该数据库中输入 -- 开启事件计划程序 SET GLOBAL …...
三种生成树(STP,RSTP,MSTP)的基本配置(自我理解)
目录 一、为什么要使用生成树(STP): 二、由于设备冗余而导致的问题: 广播风暴: 三、802.1D生成树基本配置 四、802.1D生成树实验 实验拓扑: 实验配置: 配置完成后,在SW8上观察现象&…...
FRP内网穿透,配置本地电脑作为服务器
FRP内网穿透,配置本地电脑作为服务器 下载FRP服务端客户端 参考链接: https://www.it235.com/实用工具/内网穿透/pierce.html https://www.cnblogs.com/007sx/p/17469301.html 由于没有公网ip,所以尝试内网穿透将本地电脑作为服务器ÿ…...
Linux基础指令
本文已收录至《Linux知识与编程》专栏! 作者:ARMCSKGT 演示环境:CentOS 7 目录 前言 正文 查看当前用户whoami 查看当前目录路径pwd 清理屏幕clear 查看目录下文件指令ls 进入目录指令cd 以树状结构显示目录文件tree 创建普通文件指…...
基于GRU门控循环网络的时间序列预测matlab仿真,对比LSTM网络
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 LSTM: GRU 2.算法运行软件版本 matlab2022a 3.部分核心程序 %构建GRU网络模型 layers [ ...sequenceInputLayer(N_feature)gruLayer(N_hidden)f…...
windows上ffmpeg如何录制双屏幕中的一个屏幕上的视频
首先,如何在window上安装ffmpeg自己查找scoop安装ffmpeg. 如题: 如果你有两个屏幕,如何让ffmpeg来录制其中的一个屏幕的视频呢。 很简单,首先你要查看另外一个屏幕的分辨率: 第一步:进入系统中 第二步&am…...
使用Python搭建服务器公网展示本地电脑文件
文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 Python作为热度比较高的编程语言,其语法简单且语句清晰,而且python有…...
Java IO流(五)Netty实战[TCP|Http|心跳检测|Websocket]
Netty入门代码示例(基于TCP服务) Server端 package com.bierce.io.netty.simple; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGro…...
C#基础进阶
C#基础进阶 泛型 http://www.runoob.com/csharp/csharp-generic.html 匿名函数 http://www.runoob.com/csharp/csharp-anonymous-methods.html 扩展方法 https://blog.csdn.net/u011127019/article/details/54728886 https://docs.microsoft.com/zh-cn/dotnet/csharp/pr…...
Java:ArrayList集合、LinkedList(链表)集合的底层原理及应用场景
ArrayList集合的底层原理及应用场景 LinkedList(链表)集合的底层原理及应用场景 单向链表 增加数据 删除数据 双向链表 LinkedList的应用场景之一:可以用来设计队列 入队 出队 LinkedList的应用场景之一:可以用来设计栈 压栈(push),addFirst…...
【Python】json文件的读取
文章目录 1. json简介2.json的使用规范3.json文件的书写4.json文件的读取 1. json简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于将结构化数据进行传输和存储。它基于JavaScript语法,但可以被多种编程…...
专用杂凑函数的消息鉴别码算法学习记录
声明 本文是学习github5.com 网站的报告而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 范围 GB/T 15852的本部分规定了三种采用专用杂凑函数的消息鉴别码算法。这些消息鉴别码算法可用作数据完整性检验,检验数据是否被非授权地改变。同样…...
Golang使用消息队列(RabbitMQ)
最近在使用Golang做了一个网盘项目(类似百度网盘),这个网盘项目有一个功能描述如下:用户会删除一个文件到垃圾回收站,回收站的文件有一个时间期限,比如24h,24h后数据库中记录和oss中文件会被删除…...
Apache Spark远程代码执行漏洞(CVE-2023-32007)漏洞复现
漏洞描述 Apache Spark是美国阿帕奇(Apache)基金会的一款支持非循环数据流和内存计算的大规模数据处理引擎。 Apache Spark 3.4.0之前版本存在命令注入漏洞,该漏洞源于如果ACL启用后,HttpSecurityFilter中的代码路径可以允许通过…...
春秋云镜 :CVE-2020-21650(MyuCMS后台rce)
一、题目 靶标介绍: MyuCMS开源内容管理系统,采用ThinkPHP开发而成的社区商城聚合,插件,模板,轻便快捷容易扩展 其2.2版本中admin.php/config/add方法存在任意命令执行漏洞. 进入题目: exp: url/index.p…...
测试框架pytest教程(7)实现 xunit 风格的setup
pytest支持setup和teardown,对于使用unittest和nose框架的用户来说对这些很熟悉,但是在pytest可以使用功能更强大的fixture来实现固定装置。 模块级别 如果单个模块中有多个测试函数和测试类,您可以选择实现以下固定方法,这些方…...
用队列实现栈
目录 题目题目要求示例 解答方法一、实现思路时间复杂度和空间复杂度代码 方法二、实现思路时间复杂度和空间复杂度代码 方法三、实现思路时间复杂度和空间复杂度代码 总结 题目 用队列实现栈 题目要求 题目链接 示例 解答 方法一、 使用两个队列来实现栈。 实现思路 题…...
Anolis 8.6 下 Redis 7.2.0 集群搭建和配置
Redis 7.2.0 搭建和集群配置 一.Redis 下载与单机部署1.Redis 下载2.虚拟机配置3.Redis 单机源码安装和测试4.Java 单机连接测试1.Pom 依赖2.配置文件3.启动类4.配置类5.单元测试6.测试结果 二.Redis 集群部署1.主从1.从节点配置2.Java 测试 2.哨兵1.哨兵节点配置2.复制一个哨兵…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...





