Java SPI在数据库驱动、SpringBoot自动装配中的应用

文章目录

  • 1. 初识SPI
    • 1.1 SPI的作用
    • 1.2 SPI的工作原理
    • 1.3 SPI的三个组件:Service、Service Provider、ServiceLoader
    • 1.4 SPI使用场景
    • 1.5 具体的SPI 源码分析(SPI的核心就是ServiceLoader.load()方法)
    • 1.6 SPI 的优缺点
  • 2. API、SPI、JNDI释义
  • 3. SPI应用举例1:加载数据库驱动
    • 3.1 Jdbc连接数据库示例代码
    • 3.2 为什么获取连接前不用获取驱动了,是怎么获取到驱动的?
      • 3.2.1 程序开始获取连接
      • 3.2.2 DriverManager开始初始化驱动
      • 3.2.3 调用ServiceLoader来发现驱动,使用类加载器加载jar中META-INF/services/下资源文件中的实现类
        • 3.2.3.1 oracle驱动具体实现类oracle.jdbc.OracleDriver,实现了java.sql.Driver
        • 3.2.3.2 mysql驱动具体实现类com.mysql.cj.jdbc.Driver,实现了java.sql.Driver
  • 4. SPI应用举例2:SpringBoot自动装配
    • 4.1 SpringBoot自动装配借鉴了SPI思想
    • 4.2 Spring Boot 的自动装配(Auto-configuration)原理
  • 5. Spring Boot Starter
  • 6. 只有Spring Boot Starter有spring.factories文件吗

1. 初识SPI

  • Java SPI(Service Provider Interface)是Java提供的一种服务发现机制,允许第三方为某个接口或类库提供实现。SPI的核心在于java.util.ServiceLoader类,它允许在运行时查找和加载服务的实现。
  • 一般理解是:专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。

1.1 SPI的作用

  • SPI 的合作作用:解耦。

  • SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。

  • 很多框架都使用了 Java 的 SPI 机制,比如:Spring 框架、数据库加载驱动、日志接口、以及Dubbo 的扩展实现等。

1.2 SPI的工作原理

  • 定义接口:首先,在API中定义一个接口或者抽象类作为服务接口,所有服务提供者都需要按照这个接口来实现其功能。

  • 创建配置文件:在jar包的META-INF/services目录下,创建一个以服务接口全限定名命名的文本文件,该文件中每一行写入一个实现了服务接口的具体类的全限定名。

  • 服务发现与加载:当应用程序需要使用某项服务时,通过ServiceLoader.load(ServiceInterface.class)方法动态加载服务接口的所有实现类,并将其实例化。这样,系统就可以在运行时根据不同的配置加载不同的服务实现。

  • SPI常用于框架扩展、插件机制以及模块间解耦等场景,使得系统的可扩展性和灵活性大大增强。例如,数据库驱动的加载就是Java SPI的一个典型应用案例。

对于 Java 原生 SPI,只需要满足下面几个条件:

  1. 定义服务的通用接口,针对通用的服务接口,提供具体的实现类

  2. 在 src/main/resources/META-INF/services 或者 jar包的 META-INF/services/ 目录中,新建一个文件,文件名为 接口的全名。文件内容为该接口的具体实现类的全名

  3. 将 spi 所在 jar 放在主程序的 classpath 中

  4. 服务调用方用java.util.ServiceLoader,用服务接口为参数,去动态加载具体的实现类到JVM中,然后就可以正常使用服务了

1.3 SPI的三个组件:Service、Service Provider、ServiceLoader

SPI其实是一种思想:约定大于配置

简单来说就是:jdk提供一个公共API,具体的实现由具体的应用程序自己完成,并约定一套规则来让java的类加载机制发现实现类所在位置

通过这个约定,就不需要把服务放在代码中了,通过模块被装配的时候就可以发现服务类了

  • Service:是一个公开的接口或抽象类,定义了一个抽象的功能模块(文件名称)
  • Service Provider:是Service的实现类(文件内容)
  • ServiceLoader(java.util包下):是SPI机制中的核心组件,负责在运行时发现并加载Service Provider

在这里插入图片描述

1.4 SPI使用场景

很多开源第三方jar包都有基于SPI的实现,在jar包META-INF/services中都有相关配置文件。

如下几个常见的场景:

  • JDBC加载不同类型的数据库驱动
  • Slf4j日志框架
  • Dubbo框架

看看 Dubbo 的扩展实现,就知道 SPI 机制用的多么广泛:
在这里插入图片描述

1.5 具体的SPI 源码分析(SPI的核心就是ServiceLoader.load()方法)

  1. 调用ServiceLoader.load(),创建一个ServiceLoader实例对象

  2. 创建LazyIterator实例对象lookupIterator

  3. 通过lookupIterator.hasNextService()方法读取固定目录META-INF/services/下面service全限定名文件,放在Enumeration对象configs中

  4. 解析configs得到迭代器对象Iterator pending

  5. 通过lookupIterator.nextService()方法初始化读取到的实现类,通过Class.forName()初始化

从上面的步骤可以总结以下两点

  1. 实现类工程必须创建定目录META-INF/services/,并创建service全限定名文件,文件内容是实现类全限定名

  2. 实现类必须有一个无参构造函数

1.6 SPI 的优缺点

优点

通过 SPI 机制能够大大地提高接口设计的灵活性,可以另起jar实现,不必写在一个模块中

缺点

  • 需要遍历加载所有的实现类,不能做到按需加载,这样效率还是相对较低的。
  • 当多个 ServiceLoader 同时 load 时,会有并发问题。
  • SPI 缺少实例的维护,作用域没有定义singleton和prototype的定义,不利于用户自由定制。
  • ServiceLoader不像 Spring,只能一次获取所有的接口实例, 不支持排序,随着新的实例加入,会出现排序不稳定的情况,作用域没有定- 义singleton和prototype的定义,不利于用户自由定制

2. API、SPI、JNDI释义

  • API(Application Programming Interface)就是接口,比如java.util.List 是一个接口,是一个API
  • SPI(Service Provider Interface)是一种思想
  • JNDI(Java Naming and Directory Interface)
    • 是Java平台中的一种标准服务,它提供了一组API,允许Java应用程序查找和访问命名以及目录服务。这个接口的设计目的是统一不同命名服务和目录服务的访问方式,使得开发人员无需关注具体的底层实现细节,就能在分布式环境中查找、绑定和管理资源。

    • 通过JNDI,开发者可以:

      • 命名:将对象与名称关联起来存储,并可以通过名称来获取这些对象。
      • 目录服务:除了基本的命名功能外,还支持属性查询,即根据对象的属性信息进行检索。目录服务中的条目类似于文件系统中的文件,每个条目都有名称,并且可以携带多个属性。
    • JNDI常用于以下场景:

      • 在Java EE应用服务器中,配置数据源(DataSource)、EJB引用、JMS队列和主题等资源,并通过统一的名字查找和使用它们。
        在企业级应用中,定位网络服务的位置或属性,例如LDAP(轻量级目录访问协议)目录服务中的用户账户信息。
        简而言之,JNDI是一个抽象层,它隐藏了各种各样的命名和目录服务的具体实现细节,为Java程序提供了统一、灵活且易于使用的编程接口。

3. SPI应用举例1:加载数据库驱动

依赖引用

<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>21.9.0.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>
  • SPI是JDK内置的一种动态扩展点的实现。
  • 简单来说,就是我们可以定义一个标准的接口,然后第三方的库里面可以实现这个接口。
  • 那么,程序在运行的时候,会根据配置信息动态加载第三方实现的类,从而完成功能的动态扩展机制。

解释图1:
在这里插入图片描述

解释图2:
在这里插入图片描述

  • 在Java里面,SPI机制有一个非常典型的实现案例,就是数据库驱动java.jdbc.Driver,JDK里面定义了数据库驱动类Driver,它是一个接口,JDK并没有提供实现。
  • 具体的实现是由第三方数据库厂商来完成的。在程序运行的时候,会根据我们声明的驱动类型,来动态加载对应的扩展实现,从而完成数据库的连接。

  • 除此之外,在很多开源框架里面都借鉴了Java SPI的思想,提供了自己的SPI框架,比如Dubbo定义了ExtensionLoader,实现功能的扩展。Spring提供了SpringFactoriesLoader,实现外部功能的集成。

  • 当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。通过这个约定,就不需要把服务放在代码中了,通过模块被装配的时候就可以发现服务类了。

3.1 Jdbc连接数据库示例代码

有了SPI机制之后,Class.forName(“com.mysql.jdbc.Driver”);这条语句就不需要了,

java.util.ServiceLoader会负责到jar包的META-INF/services/java.sql.Driver中获取具体驱动实现类

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JdbcDriverManagerDemo {

    static final String MYSQL_JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
    private static final String MYSQL_URL = "jdbc:mysql://localhost:3306/lelele?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC";
    private static final String USER = "root";
    private static final String PASSWORD = "admin123";


    public static void main(String[] args) {


        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            // 有了SPI机制之后,这条语句就不要了,
            // java.util.ServiceLoader会负责到jar包的META-INF/services/java.sql.Driver中获取具体驱动实现类

            // Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(MYSQL_URL, USER, PASSWORD);

            ps = conn.prepareStatement("select * from xin_stu_t_bak");
            rs = ps.executeQuery();

            while (rs.next()) {
                System.out.println(rs.getInt("id"));
            }

            // 处理查询结果
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                System.out.println("ID: " + id + ", Name: " + name);
            }

        } catch (SQLException se) {
            // 处理SQL错误
            se.printStackTrace();
        } catch (Exception e) {
            // 处理其他异常
            e.printStackTrace();
        } finally {
            // 关闭资源,确保在任何情况下都能正确关闭
            try {
                if (rs != null)
                    rs.close();
                if (ps != null)
                    ps.close();
                if (conn != null)
                    conn.close();
            } catch (SQLException se2) {
                se2.printStackTrace();
            }
        }

        System.out.println("end!");
    }
}

3.2 为什么获取连接前不用获取驱动了,是怎么获取到驱动的?

源码分析参照 本文章1.5节 具体的SPI 源码分析(SPI的核心就是ServiceLoader.load()方法)

下面是获取连接对象代码调试中的一些断点截图

3.2.1 程序开始获取连接

在这里插入图片描述

3.2.2 DriverManager开始初始化驱动

在这里插入图片描述

3.2.3 调用ServiceLoader来发现驱动,使用类加载器加载jar中META-INF/services/下资源文件中的实现类

  • 使用线程上下文类加载器(ContextClassLoader)加载

​ - 如果不做任何的设置,Java应用的线程的上下文类加载器默认就是AppClassLoader。在核心类库使用SPI接口时,传递的类加载器使用线程上下文类加载器,就可以成功的加载到SPI实现的类。线程上下文类加载器在很多SPI的实现中都会用到。

​ - 通常我们可以通过Thread.currentThread().getClassLoader()和Thread.currentThread().getContextClassLoader()获取线程上下文类加载器。

在这里插入图片描述

在这里插入图片描述

3.2.3.1 oracle驱动具体实现类oracle.jdbc.OracleDriver,实现了java.sql.Driver

在这里插入图片描述

3.2.3.2 mysql驱动具体实现类com.mysql.cj.jdbc.Driver,实现了java.sql.Driver

在这里插入图片描述

4. SPI应用举例2:SpringBoot自动装配

4.1 SpringBoot自动装配借鉴了SPI思想

Spring Factories自动装配借用了SPI机制,区别如图:

在这里插入图片描述

  • Spring Boot的自动装配机制确实与SPI(Service Provider Interface)有关联,但并不是直接使用Java标准SPI来实现自动装配。Spring Boot借鉴了SPI的思想,在其内部设计了一套更加灵活和强大的自动配置体系。

  • 在Spring Boot中,META-INF/spring.factories文件的使用方式类似于SPI的服务提供者配置文件(META-INF/services/下资源文件),它允许jar包声明自己提供的自动配置类。这些自动配置类通过条件注解(如@ConditionalOnClass、@ConditionalOnBean等)来检查应用环境并决定是否生效,从而实现了根据项目依赖和运行时环境进行自动装配的功能。

  • 因此,虽然Spring Boot没有完全采用Java SPI的标准流程,但其自动装配过程中对第三方库和服务的发现和加载机制受到了SPI思想的启发,并在此基础上进行了扩展和创新。

在这里插入图片描述

另:

META-IF/spring.factories是在Maven引入的Jar包中,每一个Jar都有自己META-IF/spring.factories,所以SpringBoot是去每一个Jar包里面寻找META-IF/spring.factories,而不是我的项目中存在META-IF/spring.factories(当然也可以存在,但是我项目的META-IF/spring.factories肯定没有类似以下这些东西)

在这里插入图片描述

4.2 Spring Boot 的自动装配(Auto-configuration)原理

@SpringBootApplication注解作用图

在这里插入图片描述

好处:简化了配置

Springboot的SPI机制是怎么实现的?

  • 程序启动,注册配置类处理器

  • Spring刷新上下文,执行配置类处理器

  • 扫描spring.factories将得到的BeanDefinition注册到容器

  • spring实例化/初始化这些BeanDefinition

Spring Boot 的自动装配(Auto-configuration)原理(上面也是)

  • 启动类与@SpringBootApplication注解:

    • Spring Boot应用通常包含一个主入口类,上面会标注@SpringBootApplication注解。
    • 这个注解包含了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解
    • 其中@EnableAutoConfiguration是开启自动装配的关键。
  • @EnableAutoConfiguration:

    • @EnableAutoConfiguration注解指示Spring Boot根据项目的classpath和已存在的Bean来自动配置应用程序。
    • 它会扫描项目依赖中所有jar包的META-INF/spring.factories文件,这些文件定义了哪些自动配置类应该被应用到当前项目中。
  • spring.factories中的自动配置类:

    • 各个Spring Boot Starter模块在它们的jar包里都提供了一个spring.factories文件

    • 该文件中列出了一系列org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的实现类全名。

      在这里插入图片描述

  • 条件化装配:

    • 自动配置类本身使用了Spring Boot提供的条件化装配机制,例如@ConditionalOnClass、@ConditionalOnMissingBean等注解。这意味着只有当满足特定条件时(如类路径中有某个类、没有用户自定义的同类型Bean等),才会实例化并注册相应的自动配置Bean。
  • 覆盖默认配置:

    • 如果开发者想要自定义某部分配置或者完全禁用某个自动配置,可以通过创建自己的配置类或在application.properties/application.yml中设置属性值来实现。Spring Boot会优先考虑用户自己定义的Bean或属性配置,从而实现了灵活的扩展和配置覆盖。
  • 自定义自动配置:

    • 开发者还可以编写自己的自动配置类,通过添加@Configuration注解,并利用条件装配注解来自定义特定场景下的自动化配置逻辑。

另:META-INF下另一个文件MANIFEST.MF的作用

  • MANIFEST.MF 是Java应用程序和JAR文件中用于存储元数据(metadata)的一个重要文件。它位于JAR文件的META-INF目录下,包含了关于该JAR包及其组件的重要信息。
  • MANIFEST.MF是Java应用打包和部署过程中不可或缺的一部分,为Java虚拟机(JVM)提供了有关如何加载和执行JAR内容的关键指导信息。

在JAR文件中,MANIFEST.MF 文件常见的用途包括:

  1. Main-Class声明:对于可执行的JAR文件(也称为Runnable JAR或Self-executable JAR),需要在MANIFEST.MF文件中指定一个主类(Main-Class)。例如:

    Main-Class: com.example.Main
    

    这样,用户可以直接通过命令行 java -jar myapp.jar 来运行这个JAR程序。

  2. Class-Path声明:用于定义当前JAR文件依赖的其他外部JAR文件的路径。例如:

    Class-Path: lib/library.jar lib/anotherlibrary.jar
    

    这样,当运行此JAR时,Java会自动加载指定路径下的库。

  3. Sealed指示:可以用来表示JAR是否被密封(sealed),即所有包内的类都必须来自同一个代码源,以增强安全性。

  4. 签名信息:如果JAR文件被数字签名,那么相关的签名证书、签名者信息等也会记录在MANIFEST.MF中。

  5. 服务提供者信息:在实现Java SPI(Service Provider Interface)时,MANIFEST.MF也可以包含ServiceProvider-Impl条目,列出实现了某个接口的服务提供者的全限定类名。

  6. OSGi Bundle信息:在OSGi框架中,每个Bundle都有自己的MANIFEST.MF文件,其中包含了Bundle的标识符、版本、导入导出的包、激活器等重要信息。

在这里插入图片描述

5. Spring Boot Starter


在这里插入图片描述

  • Spring Boot Starter 是 Spring Boot 框架中的一种模块化设计,旨在简化项目的初始化和依赖管理。它们是一组预配置的依赖项集合,每个Starter都针对特定的功能场景,例如数据访问、Web服务、安全控制等。
  • Spring Boot Starter通过预先集成并配置好了一系列功能组件,为开发者快速搭建应用提供了极大便利。
    使用Spring Boot Starter的主要优点包括:
  1. 统一入口:只需要在Maven或Gradle构建文件中引入一个Starter依赖,就可以自动获得一组相关的库以及对应的默认配置。

  2. 约定优于配置:Starter包含了基于最佳实践的默认配置,极大地减少了开发者在项目启动阶段需要编写的配置代码量。

  3. 易用性与一致性:无论开发何种功能的应用程序,都可以遵循相同的模式来引入依赖,使得团队成员易于理解和操作。

  4. 减少版本冲突:由于Starter中的所有依赖版本都已经协调一致,因此可以避免不同组件之间的版本不兼容问题。

例如,spring-boot-starter-web 包含了开发Web应用程序所需的所有基本依赖,如Spring MVC、Tomcat服务器等;而 spring-boot-starter-data-jpa 则包含了使用JPA进行数据库持久化的相关依赖。

6. 只有Spring Boot Starter有spring.factories文件吗

  • 并非只有Spring Boot Starter才有spring.factories文件。spring.factories 文件是Spring Boot框架为了实现自动配置(Auto-Configuration)和SPI(Service Provider Interface)机制而使用的一种约定。虽然它通常与Spring Boot Starter一起使用,但并非Spring Boot Starter特有的。

  • 在任何Java项目中,只要符合Spring Boot的自动装配规则,都可以创建自己的META-INF/spring.factories文件来声明自定义的自动配置类或者其他扩展点。这意味着即使不是官方提供的Spring Boot Starter模块,开发者也可以编写自己的模块,并在其jar包中包含spring.factories文件来提供额外的自动配置服务。

  • 所以,spring.factories文件不仅限于Spring Boot Starter,而是所有遵循Spring Boot规范并希望通过这种方式进行扩展的模块都可能包含的一个核心配置文件。

引用1

引用2

引用3

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《工具录》fierce

工具录 1&#xff1a;fierce2&#xff1a;选项介绍3&#xff1a;示例 本文以 kali-linux-2023.3-vmware-amd64 为例。 1&#xff1a;fierce fierce 是开源的网络安全工具&#xff0c;用于进行域名扫描和子域名枚举。 官方网址&#xff1a;https://github.com/mschwager/fierc…

基于springboot时间管理系统源码和论文

在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括时间管理系统的网络应用&#xff0c;在外国时间管理系统已经是很普遍的方式&#xff0c;不过国内的管理系统可能还处于起步阶段。时间管理系统具有时间管理功能的选择。时间管…

做好岗位设计,提升组织效率

科学地划分每个岗位&#xff0c;让员工明确自己的岗位职责&#xff0c;这样有利于提升组织的效率&#xff0c;减少无用功&#xff0c;充分发挥所有员工的正向作用。 案例素材来源于网络整理 某市电视台广告部审核科的主要职责是&#xff1a;广告内容审核、广告合同审核、广告播…

理解TCP/IP协议

一、协议 在计算机网络与信息通讯领域里&#xff0c;人们经常提及 “协议” 一词。互联网中常用的协议有HTTP、TCP、IP等。 协议的必要性 简单来说&#xff0c;协议就是计算机与计算机之间通过网络通信时&#xff0c;事先达成的一种 “约定”。这种“约定”使不同厂商的设备…

将图片添加到 PDF 的 5 种方法

需要一种称为 PDF 编辑器的特定工具才能将图片添加到 PDF。尽管大多数浏览器在查看和注释 PDF 文件方面都非常出色&#xff0c;但如果您使用图像到 PDF 技术&#xff0c;则只能将照片放入 PDF 中。无需修改即可将 PDF 文件恢复为原始格式的能力是使用此类软件程序甚至在线服务的…

Ensp AR/WLAN设备启动失败问题 错误代码41 解决方案

现象描述 启动AR设备之后&#xff0c;设备命令行无法接收输入&#xff0c;在长时间等待后一直输出“####”。启动AR/WLAN设备时&#xff0c;提示“…错误代码40…”。 检查虚拟网卡设置。 检查安装eNSP的PC上是否存在名为“VirtualBox Host-Only Network”的虚拟网卡。 - 如果…

MyBatis第三课

目录 回顾 #和$区别 #&#xff08;预编译SQL&#xff09;和$&#xff08;即时SQL&#xff0c;它是进行的字符串拼接&#xff09;的区别&#xff0c;其中之一就是预编译SQL和即时SQL的区别 原因&#xff1a; 回顾 两者的共同点 MaBits可以看作是Java程序和Mysql的沟通桥梁&…

[Kubernetes]7. K8s包管理工具Helm、使用Helm部署mongodb集群(主从数据库集群)

上一节讲解了[Kubernetes]6. k8s Pod配置管理ConfigMap & Secret以及传递环境变量的使用,k8s的命名空间以及使用kubens管理命名空间的使用,这里来介绍一下Helm的使用 一.Helm相关介绍 1.介绍 在 kubernetes 系统上部署容器化应用时需要事 先手动编写资源配置清单文件 以…

06-微服务OpenFeigh和Sentinel持久化

一、OpenFeign基础应用 1.1 概念 OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign&#xff0c;可以做到使用HTTP请求访问远程服务&#xff0c;就像调用本地方法一样的&#xff0c;开发者完全感知不到这是在调用远程方法&#xff0c;更感知不到在访…

遭受慢速连接攻击怎么办?怎么预防

慢速连接攻击是一种常见的网络攻击方式&#xff0c;其原理是利用HTTP协议的特性&#xff0c;在建立了与Http服务器的连接后&#xff0c;尽量长时间保持该连接&#xff0c;不释放&#xff0c;达到对Http服务器的攻击。 慢速连接攻击的危害包括以下几个方面&#xff1a; 1.资源…

LeetCode刷题--- 删除并获得点数

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 ​​​​​​http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述动…

学习Java API(一):基础知识点一文通✅

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读API文档注释String类创建字符串拼接字符串格式化字符串String方法substring(…

SVN切换账户

前言&#xff08;svn切换&#xff09; 本文章简单写下SVN账户切换操作 linux 1.删除目录 ~/.subversion/auth/ 下的所有文件。 2.再次操作svn时可重新输入用户名和密码。 windows (1)在工程中单击右键,单击"TortoiseSVN"。 (2)选择"Setting"。 (3)选择&quo…

数据结构【树+二叉树】

目录 线性表和非线性表 树的概念 树的存储表示 二叉树的概念 特殊二叉树 满二叉树 完全二叉树 二叉树的性质 二叉树的存储结构 顺序存储 链式存储 本篇我们开始进入数据结构中【树】的学习。 线性表和非线性表 逻辑结构&#xff1a;人想象出来的物理结构&#xf…

计算机硬件 5.1机箱与电源

第五章 其他设备 第一节 机箱与电源 一、认识电源 1.功能&#xff1a;将普通交流电转换为直流电&#xff0c;再控制电压分别输出给不同部件。 2.分类&#xff1a; 3.供电插头&#xff1a; ①8Pin插头&#xff1a;为高档PCI-E显卡供电&#xff0c;或为较新的CPU供电&#xff…

04- OpenCV:Mat对象简介和使用

目录 1、Mat对象与IplImage对象 2、Mat对象使用 3、Mat定义数组 4、相关的代码演示 1、Mat对象与IplImage对象 先看看Mat对象&#xff1a;图片在计算机眼里都是一个二维数组&#xff1b; 在OpenCV中&#xff0c;Mat是一个非常重要的类&#xff0c;用于表示图像或矩阵数据。…

南京观海微电子----时序图绘制工具

Wavedrom 是一款功能强大且简单易用的文本转图表工具&#xff0c;被广泛应用于生成时序图、波形图等交互式波形。其特点在于使用简单的文本语法&#xff0c;使得开发人员能够以可视化的方式表示数字信号和时间序列数据。Wavedrom 的优势在于其高度灵活性和可扩展性&#xff0c;…

Java多线程并发篇----第十二篇

系列文章目录 文章目录 系列文章目录前言一、ReentrantLock二、Condition 类和 Object 类锁方法区别区别三、tryLock 和 lock 和 lockInterruptibly 的区别前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章…

爬虫—抓取表情党热门栏目名称及链接

爬虫—抓取表情党热门栏目名称及链接 表情党网址&#xff1a;https://qq.yh31.com/ 目标&#xff1a;抓取表情党主页的热门栏目名称及对应的链接&#xff0c;如下图所示&#xff1a; 按F12&#xff08;谷歌浏览器&#xff09;&#xff0c;进入开发者工具模式&#xff0c;进行…

鸿蒙OS应用开发之仪表组件

在鸿蒙系统里定义了一个Gauge组件,在这里估且叫做仪表组件,它是实现一个环形展示数据的组件,其实也可以用来指示方向的一个组件。它简单的形状如下: 这个组件是一个360度可以设置的圆环,它可以设置每一段的颜色。在这里设置了四段颜色,每段占25%的长度。 这个组件接口定义…
最新文章