Spring IoC——针对实习面试

目录

  • Spring IoC
    • 谈谈你对Spring IoC的理解
    • IoC和DI有区别吗?
      • IoC(控制反转)
      • DI(依赖注入)
      • IoC与DI的区别
    • 什么是Spring Bean?作用域有哪些?
    • Bean是线程安全的吗?
    • 说一下Spring Bean的生命周期
    • 注入Bean的方式有哪些?

Spring IoC

在这里插入图片描述

谈谈你对Spring IoC的理解

Spring IoC(Inversion of Control,控制反转)是Spring框架的核心特性之一,它提供了一种机制,允许对象之间的依赖关系被自动管理,而不是通过硬编码的方式在组件内部直接创建或查找它们的依赖关系。控制反转是一种设计原则,而不是一个具体的技术实现,用来减少计算机程序的耦合度提高模块间的独立性

在Spring IoC中,主要涉及以下几个概念:

  1. 容器(Container):Spring IoC容器是一个管理对象生命周期和依赖关系的系统。它负责创建对象、配置对象、组装对象,并管理对象的整个生命周期。

  2. Bean(豆):在Spring中,由IoC容器管理的对象被称为Bean。这些Bean由容器实例化、配置和组装,并且它们通常由容器进行管理。

  3. 依赖注入(Dependency Injection, DI):这是实现控制反转的主要方式之一。在依赖注入中,一个对象的依赖关系不是由对象自己创建,而是由外部容器注入。这种方式可以是构造器注入、字段注入或setter方法注入。

  4. 配置元数据(Configuration Metadata):这些是定义了Bean如何创建和组装的信息。在Spring中,可以通过XML配置文件、注解(如@Component@Service@Repository等)或者Java配置类来提供这些元数据。

  5. 生命周期回调(Lifecycle Callbacks):Spring允许在Bean的生命周期中的特定点执行自定义代码,例如初始化(@PostConstruct)和销毁(@PreDestroy)阶段。

  6. 作用域(Scope):Spring IoC容器可以管理不同作用域的Bean,例如单例(Singleton)、原型(Prototype)、请求(Request)、会话(Session)等。

  7. 自动装配(Autowiring):Spring提供了自动装配功能,可以根据一定的规则(如类型匹配、名称匹配等)自动将依赖关系注入到Bean中。

Spring IoC的主要优点包括:

  • 降低耦合度:由于依赖关系是由容器管理的,因此组件之间的耦合度降低。
  • 提高模块化:每个组件都可以独立于其他组件进行开发和测试。
  • 代码重用:通过将对象的创建和配置从使用它们的代码中分离出来,可以更容易地重用代码。
  • 易于测试:由于对象的依赖关系是由容器管理的,因此在测试时可以轻松地替换依赖关系,进行单元测试。

总的来说,Spring IoC提供了一种灵活、可配置的方式来管理应用程序中的对象,使得开发人员可以更专注于业务逻辑,而不是对象的创建和管理

IoC和DI有区别吗?

IoC(控制反转)和DI(依赖注入)这两个概念经常一起出现,它们在很多情况下被交替使用,但实际上它们是两个相关但不同的概念。

IoC(控制反转)

IoC是一种设计原则,它的核心思想是将对象的创建和它们之间的依赖关系的控制权从对象本身转移到外部容器。这样做的目的是为了降低系统的耦合度,提高模块化和可维护性。在IoC中,对象不负责查找或创建它们的依赖,而是通过容器来提供这些依赖。

DI(依赖注入)

DI是实现IoC的一种具体技术手段。在DI中,一个对象的依赖关系被外部源(通常是IoC容器)“注入”到对象中。这种注入可以发生在对象的生命周期中的不同点,比如在对象创建时通过构造器注入、在对象的属性设置时通过setter方法注入,或者在对象的某个字段直接注入。

IoC与DI的区别

  • 概念层面:IoC是一个更广泛的概念,它描述了一种对象创建和依赖管理的模式。DI是实现IoC的一种方式,它专注于如何将依赖关系注入到对象中。

  • 实现方式:IoC可以通过多种方式实现,包括依赖注入、服务定位器模式等。而DI特指通过构造器、setter方法或字段注入等方式将依赖关系注入到对象中。

  • 关注点:IoC关注的是如何管理对象的生命周期和它们之间的依赖关系,而DI关注的是如何将这些依赖关系具体地注入到对象中

在实际应用中,DI是实现IoC最常见的方式,尤其是在Spring框架中,DI被广泛用于管理Bean的依赖关系。因此,虽然IoC和DI在概念上有所区别,但在很多情况下,它们被紧密地联系在一起,DI成为了实现IoC的首选方式。

什么是Spring Bean?作用域有哪些?

在Spring框架中,Spring Bean 是由Spring IoC容器管理的对象。这些对象由容器实例化、配置和组装,并且它们通常由容器进行管理。Spring Bean是构成Spring应用的基本构建块,它们可以是服务、数据访问对象(DAO)、控制器、工具类等。

Spring Bean的作用域

Spring Bean可以有不同的作用域,这些作用域定义了Bean的生命周期和在不同请求或会话中的可见性。以下是Spring支持的主要作用域:

  1. singleton(单例)

    • 这是默认的作用域。在Spring IoC容器中,每个由容器管理的Bean定义对应一个唯一的Bean实例。Singleton Beans是线程安全的,并且在整个容器的生命周期内只创建一次。
  2. prototype(原型)

    • 对于prototype作用域的Bean,每次请求(例如通过容器的getBean方法)都会创建一个新的Bean实例。Prototype Beans不是线程安全的,并且它们的生命周期很,通常由容器创建后立即释放。
  3. request(请求)

    • 每个HTTP请求都会创建一个新的Bean,并且该Bean仅在请求的生命周期内有效。这些Bean只在当前请求内可见,对于Web应用程序来说非常有用。
  4. session(会话)

    • 一个会话对应一个Bean实例,Spring容器为每个HTTP Session创建一个Bean实例。这些Bean在会话的生命周期内有效。
  5. application(应用程序)

    • 一个Web应用程序对应一个Bean实例,该Bean在应用程序的整个生命周期内有效,直到Web应用程序被卸载。
  6. websocket(WebSocket)

    • 一个WebSocket对应一个Bean实例,该Bean在WebSocket的生命周期内有效。

除了这些标准作用域,Spring还允许通过实现org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope方法来注册自定义作用域。

作用域的选择

选择Bean的作用域通常取决于应用程序的需求和Bean的用途
例如,对于需要共享状态的服务,可能会选择singleton作用域,而对于每个用户请求都需要独立实例的服务,则可能会选择prototype作用域。对于Web应用程序requestsession作用域常用于管理与特定请求或用户会话相关的数据。

Bean是线程安全的吗?

在Spring框架中,Bean是否线程安全并不是由Spring容器直接决定的,而是由Bean的实现和使用方式决定的。以下是一些关于Spring Bean线程安全的关键点:

  1. 单例Bean(singleton)

    • 默认情况下,Spring容器中的单例Bean是线程不安全的,因为它们是全局共享的。如果多个线程共享同一个Bean实例,并且该实例包含非线程安全的成员变量,那么在并发访问时可能会出现问题。
    • 要使单例Bean线程安全,需要确保Bean的实现是线程安全的,或者通过同步代码块、使用线程安全的集合类等方式来管理并发访问。
  2. 原型Bean(prototype)

    • 原型作用域的Bean在每次请求时都会创建一个新的实例,因此它们天生是线程安全的,因为每个线程或请求都会获得自己的Bean实例,不存在共享状态的问题。
  3. 其他作用域(如request、session、application等)

    • 这些作用域的Bean在特定的上下文(如HTTP请求、会话或应用程序)内是唯一的,因此它们在该上下文中是线程安全的。但是,如果Bean内部包含非线程安全的成员变量或方法,那么在并发访问时仍然可能出现问题。
  4. 线程安全的实现

    • 如果需要确保Bean的线程安全,可以通过以下方式实现:
      • 使用同步代码块或方法来管理对共享资源的访问。
      • 使用线程安全的集合类,如ConcurrentHashMapCopyOnWriteArrayList等。
      • 确保Bean的业务逻辑是无状态的,或者状态不共享给多个线程。
      • 使用不可变对象,因为不可变对象本质上是线程安全的。
  5. Spring提供的线程安全支持

    • Spring提供了一些工具和注解来帮助实现线程安全,例如@Transactional注解可以确保数据库操作的原子性和一致性,从而间接提供线程安全。

总之,Bean的线程安全取决于其实现和使用方式。即使是单例Bean,也可以通过适当的设计和同步机制来实现线程安全。而对于原型Bean和其他作用域的Bean,虽然它们在特定上下文中是唯一的,但仍需要确保Bean内部的实现是线程安全的。

说一下Spring Bean的生命周期

  1. 实例化(Instantiation)
    • Spring容器通过反射机制使用Bean的默认构造函数(如果没有其他特殊的构造函数定义)来创建Bean的实例。这是Bean生命周期的开始阶段。例如,假设有一个简单的Java类UserService
    public class UserService {public UserService() {System.out.println("UserService正在被实例化");}
    }
    
    • 当这个UserService被定义为Spring Bean并由容器管理时,容器会调用这个构造函数来创建对象实例。
  2. 属性赋值(Populate Properties)
    • 在实例化之后,Spring容器会根据在配置文件(如XML配置或基于Java的配置)中定义的属性值来填充Bean的属性。如果使用基于注解的配置,例如@Value注解可以用于注入简单的值,@Autowired等注解可以用于注入其他Bean的引用。
    • 假设UserService有一个属性userRepository,配置如下:
    @Service
    public class UserService {@Autowiredprivate UserRepository userRepository;//...
    }
    
    • Spring会在这个阶段找到UserRepository的实例,并将其注入到UserServiceuserRepository属性中。
  3. BeanNameAware接口回调(如果实现了该接口)
    • 如果Bean实现了BeanNameAware接口,Spring容器会调用setBeanName方法,将Bean的名称传递给Bean实例。这使得Bean能够知道自己在容器中的名称。例如:
    public class MyBean implements BeanNameAware {private String beanName;@Overridepublic void setBeanName(String name) {this.beanName = name;System.out.println("Bean名称是: " + name);}
    }
    
  4. BeanFactoryAware接口回调(如果实现了该接口)
    • 当Bean实现了BeanFactoryAware接口时,Spring会调用setBeanFactory方法,将创建它的BeanFactory实例注入到Bean中。这样Bean就可以通过这个工厂来获取其他Bean等操作。
    public class MyBean implements BeanFactoryAware {private BeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;System.out.println("已注入BeanFactory");}
    }
    
  5. ApplicationContextAware接口回调(如果实现了该接口)
    • 类似地,如果Bean实现了ApplicationContextAware接口,Spring会调用setApplicationContext方法,将ApplicationContext实例注入到Bean中。这使得Bean可以访问容器的各种资源,如获取其他Bean、发布事件等。
    public class MyBean implements ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;System.out.println("已注入ApplicationContext");}
    }
    
  6. BeanPostProcessor的前置处理方法(postProcessBeforeInitialization)调用
    • Spring会遍历所有注册的BeanPostProcessor,并调用它们的postProcessBeforeInitialization方法,对Bean进行前置处理。这些处理器可以对Bean进行一些修改,如添加额外的属性或方法调用等。
    • 例如,自定义一个BeanPostProcessor
    public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("在初始化之前处理Bean: " + beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("在初始化之后处理Bean: " + beanName);return bean;}
    }
    
  7. InitializingBean接口回调(如果实现了该接口)或自定义初始化方法调用
    • 如果Bean实现了InitializingBean接口,Spring会调用afterPropertiesSet方法,用于执行一些初始化操作。另外,也可以通过在配置中指定init - method来定义一个自定义的初始化方法。
    • 例如,对于实现InitializingBean的Bean:
    public class MyBean implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("执行InitializingBean的初始化方法");}
    }
    
    • 或者通过XML配置自定义初始化方法:
    <bean id="myBean" class="com.example.MyBean" init - method="customInitMethod">
    </bean>
    
    • 对应的Java方法:
    public class MyBean {public void customInitMethod() {System.out.println("执行自定义初始化方法");}
    }
    
  8. BeanPostProcessor的后置处理方法(postProcessAfterInitialization)调用
    • 与前置处理类似,Spring会再次遍历BeanPostProcessor,调用它们的postProcessAfterInitialization方法,对Bean进行后置处理。这是Bean初始化后的最后一次处理机会。
  9. Bean的使用(Usage)
    • 此时,Bean已经完全初始化可以被应用程序使用。它可以被注入到其他Bean中,或者被容器管理的其他组件调用其方法来完成业务逻辑。
  10. 销毁(Destruction)
  • 当容器关闭或者Bean不再需要时(例如在一个Web应用中,当应用服务器关闭时),如果Bean实现了DisposableBean接口,Spring会调用destroy方法。同时,如果在配置中指定了destroy - method(如在XML配置中),也会执行这个自定义的销毁方法。
  • 例如,对于实现DisposableBean的Bean:
public class MyBean implements DisposableBean {@Overridepublic void destroy() throws Exception {System.out.println("执行DisposableBean的销毁方法");}
}
  • 或者通过XML配置自定义销毁方法:
<bean id="myBean" class="com.example.MyBean" destroy - method="customDestroyMethod">
</bean>
  • 对应的Java方法:
public class MyBean {public void customDestroyMethod() {System.out.println("执行自定义销毁方法");}
}

事实上这部分过程复杂,实习面试应该不会问这么深的问题,了解即可

注入Bean的方式有哪些?

  1. 构造函数注入(Constructor Injection)
    • 原理:通过Bean的构造函数来注入依赖。当容器创建Bean实例时,会根据构造函数的参数类型和数量,查找匹配的Bean来进行注入。
    • 示例
    public class UserService {private UserRepository userRepository;public UserService(UserRepository userRepository) {this.userRepository = userRepository;}
    }
    
    • 在这个例子中,UserService的构造函数接受一个UserRepository类型的参数。当Spring容器创建UserService实例时,会查找UserRepository类型的Bean,并将其注入到UserService的构造函数中。这种方式的优点是可以保证Bean在创建时所有必需的依赖都已经注入,对象的状态是完整的。
  2. Setter方法注入(Setter Injection)
    • 原理:使用Bean的Setter方法来注入依赖。Spring容器会在Bean实例化后,通过调用Setter方法将依赖的Bean注入进去。
    • 示例
    public class UserService {private UserRepository userRepository;public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}
    }
    
    • 在这里,Spring容器会先创建UserService的实例,然后查找UserRepository类型的Bean,通过调用setUserRepository方法将其注入。这种方式的优点是更加灵活,允许在对象创建后再注入依赖,例如可以在某些条件下重新设置依赖。
  3. 字段注入(Field Injection)
    • 原理:直接在Bean的字段上使用注解(如@Autowired)来注入依赖。Spring容器会通过反射机制将依赖的Bean注入到对应的字段中。
    • 示例
    public class UserService {@Autowiredprivate UserRepository userRepository;
    }
    
    • 这种方式是最简洁的,但也有一些缺点。例如,它使得单元测试变得困难,因为无法通过构造函数或Setter方法来提供模拟的依赖。不过在简单的应用场景下,它可以快速地实现依赖注入。
  4. 接口注入(Interface Injection)
    • 原理:通过实现特定的接口来完成依赖注入。容器会根据接口的定义来注入相应的实现类。不过这种方式在Spring中使用较
    • 示例(假设存在这样的接口和实现)
    • 定义接口:
    public interface Injectable {void injectDependency(Object dependency);
    }
    
    • 实现类:
    public class MyBean implements Injectable {private Object dependency;@Overridepublic void injectDependency(Object dependency) {this.dependency = dependency;}
    }
    
    • 在这种方式下,容器需要识别Injectable接口,并调用injectDependency方法来注入依赖。在实际的Spring应用中,这种方式因为比较复杂,没有构造函数注入、Setter注入和字段注入那么常用。

在Spring框架中,最常用的是构造函数注入Setter方法注入,尤其是在遵循依赖注入最佳实践和编写可测试代码时,这两种方式更受青睐。字段注入虽然简洁,但在某些场景下可能会带来一些维护和测试上的问题。

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

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

相关文章

【H2O2|全栈】MySQL的云端部署

目录 前言 开篇语 准备工作 MySQL移除 为什么需要移除&#xff1f; 移除操作 Yum仓库 yum简介 rpm安装 yum库安装 MySQL安装 使用yum安装 开机自启动 检查运行状态 MySQL配置 初始密码 ​编辑登录 修改root密码 退出MySQL 字符集配置 结束语 前言 开篇语…

数据结构-二叉平衡树

一.平衡二叉树 二叉搜索树插入的次序不同导致不同的深度和平均查找长度ASL 左右子树高度差不超过绝对值1的二叉搜索是二叉平衡树 二.平衡二叉树的调整 在右子树的右子树上的插入做RR插入 把被破坏节点的右子树变成跟节点并把这个右子树的左子树挂载到原来被破坏的结点的右子树…

【PCIE716-0】基于PCIe总线架构的XC7Z100 FPGA高性能实时信号处理平台

板卡概述 PCIE716-0是一款基于PCIe总线架构的XC7Z100 FPGA高性能实时信号处理平台。该平台采用Xilinx的ZYNQ SOC系列产品XC7Z100作为主处理器。 该平台的PL端具有1个FMC&#xff08;HPC&#xff09;接口&#xff0c;1路PCIe x8主机接口&#xff0c;支持1路UART串口、支持1组6…

从0开始的数据结构速过——番外(1)

目录 尝试 思考与架构设置 编写&#xff01; 一些额外知识的补充 可变参数模板 std::common_type std::forward 这是《数据结构从0开始》的一个番外。实际上是介绍一下一些现代C的写法。这里以快速构建std::array作为契机来说明一下一些现代C的语法。 尝试 我们在这里呢…

Day10_CSS过度动画

Day10_CSS过度动画 背景 : PC和APP项目我们已经开发完毕, 但是再真正开发的时候有些有些简易的动态效果我们可以使用CSS完成 ; 本节课我们来使用CSS完成基础的动画效果 今日学习目标 CSS3过度CSS3平面动态效果CSS3动画效果案例 1. CSS3过渡 ​ 含义 :过渡指的是元素从一种…

如何制作代购系统的客服支持模块

在代购系统中&#xff0c;客服支持模块是维护用户满意度和忠诚度的关键环节。一个有效的客服支持模块不仅可以解决用户的疑问和问题&#xff0c;还可以收集用户反馈&#xff0c;用于改进服务和产品。本文将详细介绍如何制作一个代购系统的客服支持模块&#xff0c;包括前端界面…

【unity小技巧】一些unity3D灯光的使用与渲染及性能优化方案

文章目录 天空盒反射配置太阳耀斑眩光烘培光照烘培光照时弹出错误&#xff0c;记得勾选模型下面的选择阴影项目配置光源模型模型shader的问题 全局光照混合光照模式混合照明模式减性照明模式Shadowmask照明模式间接烘焙照明模式 环境光遮罩灯光探针反射探针技术关闭反射探针可以…

Spring Boot汽车资讯:科技与汽车的对话

5系统详细实现 5.1 管理员模块的实现 5.1.1 用户信息管理 汽车资讯网站的系统管理员可以管理用户&#xff0c;可以对用户信息修改删除审核以及查询操作。具体界面的展示如图5.1所示。 图5.1 用户信息管理界面 5.1.2 汽车品牌管理 系统管理员可以汽车品牌信息进行添加&#xf…

go 学习网站,go例子 go demo go学习视频

1. 代码例子&#xff1a; Go by Example 2. b站 视频&#xff1a; 尚硅谷视频&#xff1a; 004_尚硅谷_程序的基本概念_哔哩哔哩_bilibili 3. go技术文档&#xff1a; fmt Go语言中文文档

记录下,用油猴Tampermonkey监听所有请求,绕过seesion

油猴Tampermonkey监听所有请求&#xff0c;绕过seesion 前因后果脚本编写 前因后果 原因是要白嫖一个网站的接口&#xff0c;这个接口的页面入口被隐藏掉了&#xff0c;不能通过页面调用&#xff0c;幸好之前有想过逆向破解通过账号密码模拟登录后拿到token&#xff0c;请求该…

网络安全:我们的安全防线

在数字化时代&#xff0c;网络安全已成为国家安全、经济发展和社会稳定的重要组成部分。网络安全不仅仅是技术问题&#xff0c;更是一个涉及政治、经济、文化、社会等多个层面的综合性问题。从宏观到微观&#xff0c;网络安全的重要性不言而喻。 宏观层面&#xff1a;国家安全与…

多账号登录管理器(淘宝、京东、拼多多等)

目录 下载安装与运行 解决什么问题 功能说明 目前支持的平台 功能演示 登录后能保持多久 下载安装与运行 下载、安装与运行 语雀 解决什么问题 多个账号的快捷登录与切换 功能说明 支持多个电商平台支持多个账号的登录保持支持快捷切换支持导入导出支持批量删除支持…

浅谈网络 | 二层到三层

目录 物理层到MAC层第一层&#xff08;物理层&#xff09;第二层&#xff08;数据链路层&#xff09;局域网 交换机与VLAN生成树协议VLAN ICMP与pingICMP 协议的格式 网关静态路由是什么&#xff1f; 路由协议如何配置策略路由&#xff1f;动态路由算法动态路由协议 物理层到MA…

c++ 后端

基础知识 1. 指针、引用2. 数组3. 缺省参数4. 函数重载5. 内联函数6. 宏7. auto8. const9. 类和对象10. 类的6个默认成员函数11. 初始化列表12. this指针13. C/C的区别14. C 三大特性15. 结构体内存对齐规则16. explicit17. static18. 友元类、友元函数19. 内部类20. 内存管理&…

汽车资讯新趋势:Spring Boot技术解读

5系统详细实现 5.1 管理员模块的实现 5.1.1 用户信息管理 汽车资讯网站的系统管理员可以管理用户&#xff0c;可以对用户信息修改删除审核以及查询操作。具体界面的展示如图5.1所示。 图5.1 用户信息管理界面 5.1.2 汽车品牌管理 系统管理员可以汽车品牌信息进行添加&#xf…

【C++】vector

一、vector的介绍及使用 1.1 vector的介绍 vector的底层与string相似都是顺序表形式管理数组&#xff0c;本质上来说string就可以归入到vector里面&#xff0c;但是在实际使用中&#xff0c;字符有很多自身独有的接口设计需要&#xff0c;因此string被单独拿出来设计。在前面s…

uniapp Uview上传图片组件Upload会自动刷新

背景 最近在做跑团小程序&#xff0c;马上接近尾声了&#xff0c;今天新增一个团长增加活动页面&#xff1a; 然后一切准备就绪&#xff0c;发现了一个问题&#xff0c;当选择上传图片后&#xff0c;页面会自动刷新&#xff0c;把之前填写的信息全部重置了。奇怪了&#xff0c…

软件测试之缺陷管理

一、软件缺陷的基本概念 1、软件缺陷的基本概念主要分为&#xff1a;缺陷、故障、失效这三种。 &#xff08;1&#xff09;缺陷&#xff08;defect&#xff09;&#xff1a;存在于软件之中的偏差&#xff0c;可被激活&#xff0c;以静态的形式存在于软件内部&#xff0c;相当…

数字资产与大健康领域的知识宝藏:高效知识库搭建策略

在数字化时代&#xff0c;大健康领域的企业积累了丰富的数字资产&#xff0c;这些资产如同一座待挖掘的金矿&#xff0c;蕴含着巨大的价值。高效搭建知识库&#xff0c;能够将这些数字资产转化为企业竞争力。 数字资产与大健康领域知识宝藏 数字资产在大健康领域包括患者数据…

使用WebRTC实现点对点实时音视频通信的技术详解

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用WebRTC实现点对点实时音视频通信的技术详解 使用WebRTC实现点对点实时音视频通信的技术详解 使用WebRTC实现点对点实时音视频…