教程

Java SASL 入门

1、概览 本文将带你了解 Simple Authentication and Security Layer(简单身份验证和安全层,SASL)的基础知识,以及如何在 Java 中使用 SASL 确保通信安全。 主要是使用 SASL 来确保客户端和服务端通信的安全。 2、SASL 是什么? SASL 是互联网协议中的身份验证和数据安全框架。它旨在将互联网协议与特定的身份验证机制分离开来。 通信安全的需求是不言而喻的。从客户端和服务器通信的角度可以更好的理解这一点。客户端和服务器通常通过网络交换数据。双方必须相互信任并安全地发送数据。 2.1、SASL 的定位是什么? 在应用中,我们可能使用 SMTP 发送电子邮件,使用 LDAP 访问目录服务。但每种协议都可能支持另一种身份验证机制,如 Digest-MD5 或 Kerberos。 如果有一种方法可以让协议以声明的方式交换身份验证机制,那会怎么样呢?这正是 SASL 的用武之地。支持 SASL 的协议总是可以支持任何一种 SASL 机制。 因此,应用可以协商一个合适的机制,并采用该机制进行身份验证和安全通信。 2.2、SASL 的原理是什么? 了解了 SASL 在整个 安全通信 中的位置后,让我们来了解一下它是如何工作的。 SASL 是一种 challenge-response 框架。在这里,服务器向客户端发出 challenge,客户端根据 challenge 发送响应。challenge 和响应是任意长度的字节数组,因此可以携带任何特定机制的数据。 这种交换会持续多次,最后在服务器不再发出 exchange 时结束。 此外,客户端和服务器还可以在验证后协商一个安全层(Security Layer)。所有后续通信都可以利用这个安全层。不过,需要注意的是,有些机制可能只支持身份验证。 重要的是要明白,SASL 只提供了一个交换 exchange 和响应数据的框架。它没有提及任何有关数据本身或如何交换数据的内容。这些细节留给采用 SASL 的应用去处理。 3、Java 中的 SASL 支持 Java 中有一些 API,可支持使用 SASL 开发客户端和服务器端应用。API 并不依赖于实际机制本身。使用 Java SASL API 的应用可根据所需的安全功能选择一种机制。

使用 Spring AI 和 PGVector 实现语义搜索

1、概览 搜索 是软件中的一个基本概念,目的是在大数据集中查找相关信息。它涉及在一系列项目中查找特定项目。 本文将带你了解如何使用 Spring AI、PGVector 和 Ollama 实现语义搜索。 2、基础概念 语义搜索是一种先进的搜索技术,它利用词语的含义来查找最相关的结果。要构建语义搜索应用,需要了解一些关键概念: 词嵌入(Word Embedding):词嵌入是一种词表示类型,允许具有相似含义的单词具有类似的表示形式。词嵌入将单词转换为数值向量,可以在机器学习模型中使用。 语义相似性:语义相似性是衡量两段文本在意义上相似程度的一种方法。它用于比较单词、句子或文档的含义。 向量空间模型:向量空间模型是一种数学模型,用于将文本文档表示为高维空间中的向量。在该模型中,每个单词都表示为一个向量,两个单词之间的相似度根据它们向量之间的距离来计算。 余弦相似度:余弦相似度是内积空间中两个非零向量之间的相似度量,它测量的是两个向量之间角度的余弦值。它计算向量空间模型中两个向量之间的相似度。 3、先决条件 首先,我们需要在机器上安装 Docker,以便运行 PGVector 和 Ollama。 然后,在 Spring 应用中添加 Spring AI Ollama 和 PGVector 依赖: <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId> </dependency> 还要添加 Spring Boot 的 Docker Compose 支持,以管理 Ollama 和 PGVector Docker 容器: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-docker-compose</artifactId> <version>3.1.1</version> </dependency> 除了依赖外,还要在 docker-compose.yml 文件中描述这两个服务: services: postgres: image: pgvector/pgvector:pg17 environment: POSTGRES_DB: vectordb POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres ports: - "5434:5432" healthcheck: test: [ "CMD-SHELL", "pg_isready -U postgres" ] interval: 10s timeout: 5s retries: 5 ollama: image: ollama/ollama:latest ports: - "11435:11434" volumes: - ollama_data:/root/.

在 JDBC 的一个查询中执行多条 SQL 语句

1、概览 在使用 Java 和 JDBC 处理数据库时,有时我们需要将多条 SQL 语句作为单个操作来执行。这可以提高应用性能、确保原子性或更有效地管理复杂的工作流。 本文将带你了解如何使用 Statement 对象、批处理和存储过程来演示如何高效执行多个 SQL 查询。 在本教程中,我们将使用 MySQL 作为数据库。 2、JDBC 和数据库设置 2.1、Maven 依赖 首先,在 pom.xml 中添加以下依赖项,以包含 MySQL JDBC 驱动: <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId <version>8.0.33</version> </dependency> 2.2、数据库配置 在本例中,我们要创建一个名为 user_db 的 MySQL 数据库和一个名为 users 的表,并为其执行多个 insert 查询: CREATE DATABASE user_db; USE user_db; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL UNIQUE ); 数据库设置完毕后,我们就可以使用 JDBC 运行多条 SQL 语句了。

Java 中的货币(Currency)API

1、概览 JSR 354 定义了 Java 中涉及 “货币和金钱” 的标准 API。 其目标是为 Java 生态系统添加一个灵活、可扩展的 API,使货币的处理更简单、更安全。 该 JSR 并未进入 JDK 9,但已成为未来 JDK 版本的候选。 2、设置 首先,在 pom.xml 中定义依赖: <dependency> <groupId>org.javamoney</groupId> <artifactId>moneta</artifactId> <version>1.1</version> </dependency> 最新版的依赖可以 这里 找到。 3、JSR-354 特性 货币 API 的目标是: 提供处理和计算货币金额的 API 定义表示货币和货币金额以及货币舍入的类 处理货币汇率 处理货币和金额的格式化和解析 4、Model JSR-354 规范的主要类如下图所示: Model 包含两个主要接口:货币单位(CurrencyUnit)和货币数量(MonetaryAmount),下文将对此进行解释。 5、CurrencyUnit CurrencyUnit 模拟了货币的最基本属性。可使用 Monetary.getCurrency 方法获取其实例: @Test public void givenCurrencyCode_whenString_thanExist() { CurrencyUnit usd = Monetary.getCurrency("USD"); assertNotNull(usd); assertEquals(usd.getCurrencyCode(), "USD"); assertEquals(usd.getNumericCode(), 840); assertEquals(usd.getDefaultFractionDigits(), 2); } 我们创建 CurrencyUnit 时使用的是货币的字符串表示法,如果使用不存在的货币代码创建货币会导致 UnknownCurrency 异常:

解决 JDBC 异常 “Cannot issue data manipulation statements with executeQuery()”

1、简介 本文将带你了解如何解决 JDBC 异常:“Cannot issue data manipulation statements with executeQuery()”。 在使用 JDBC 与数据库交互时,这个异常并不常见,但这个问题很容易解决。 2、理解异常 2.1、导致这个异常的原因 当我们尝试使用 executeQuery() 方法执行 INSERT、UPDATE 或 DELETE 语句时,就会出现错误 “Cannot issue data manipulation statements with executeQuery()” 异常。 Statement 或 PreparedStatement 对象中的 executeQuery() 方法专门用于处理 SELECT 查询。如果检查一下该方法的签名,就会发现它返回的是 ResultSet 实例,其中包含从数据库中获取的记录。 使用 Connector/J 连接 MySQL 时会出现这种异常,但其他数据库也执行相同的规则。在这种情况下,它们会以不同的错误信息抛出类似的错误。 需要注意的是,在较新版本的 MySQL Connector/J 中,该错误信息已略有更新。现在是这样: java.sql.SQLException: Statement.executeQuery() cannot issue statements that do not produce result sets. 2.2、导致该异常的场景 来看一个代码示例,以便更好地理解是什么触发了异常。如前所述,我们使用 MySQL 数据库。 首先,为示例创建一个简单的数据表: CREATE TABLE IF NOT EXISTS users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50), email VARCHAR(50) ) 现在我们可以尝试执行一个非 SELECT 语句的查询。

通过命令行执行 WAR 包

1、概览 WAR 文件是 Web 应用程序归档文件(Web Application Archive)或 Web 应用程序资源文件(Web Application Resource)的简称,用于存储 Java Web 应用程序的资源。WAR 将所有 Web 组件打包成一个单元。它包含 JAR 文件、JavaServer Page(JSP)、Java Servlet、Java 类文件、XML 文件、HTML 文件以及 Web 应用程序所需的其他资源。 本文将带你了解如何使用 CLI 调用在 WAR 文件中的类。 2、WAR 文件的结构 WAR 文件使用 .war 扩展名并打包 Web 应用程序,我们可以将其部署到任何 Servlet/JSP 容器上。 下面是一个典型的 WAR 文件结构布局示例: META-INF/ MANIFEST.MF WEB-INF/ web.xml jsp/ helloWorld.jsp classes/ com/baeldung/*.class application.properties static/ templates/ lib/ // third party *.jar files as libs index.html 内部有一个 META-INF 目录,在 MANIFEST.MF 中保存了有关 Web 存档的有用信息。META-INF 目录是私有的,外部无法访问。

解决 Java 异常 “ClassCastException: Ljava.lang.Object; cannot be cast to Ljava.lang.Integer”

1、概览 在 Java 中,数组是语言的基本组成部分,它提供了一种结构化的方式来存储相同类型的多个值。然而,在使用数组和类型转换时,我们有时会遇到意想不到的运行时异常。 当我们试图将 Object[] 数组转换为特定数组类型(如 Integer[])时,就会出现这样的问题。这会导致 ClassCastException 异常,这可能会让很多人感到困惑。 本文将带你了解出现这种异常的原因,从而了解 Java 数组的基本机制,并学习如何在代码中避免此类错误。 2、问题介绍 像往常一样,先通过一个例子来了解这个问题: Integer[] convertObjectArray() { Object[] objArray = new Object[3]; objArray[0] = 1; objArray[1] = 2; objArray[2] = 3; return (Integer[]) objArray; } 在上述方法中,我们在一个 Object[] 数组中插入了三个 int 值。由于 Object[] 数组只包含 Integer,我们尝试将其转换为 Integer[] 数组。 在测试中调用该方法,看看会发生什么: Exception ex = assertThrows(ClassCastException.class, () -> convertObjectArray()); LOG.error("The exception stacktrace:", ex); 可以看到,调用该方法会抛出 ClassCastException。在输出中,还可以看到异常的详细信息: java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Integer; ... at .

在 Spring 中校验 List 参数

1、概览 在 Spring 中,可以通过内置工具和注解简化数据验证,使我们能够轻松实现强大的验证逻辑。 本文将带你了解如何在 Spring 中验证 List 类型的参数值。 2、在 Spring 中配置 Validation 要启用验证,需要在 pom.xml 中添加以下 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 该依赖会自动将 Hibernate Validator 整合到我们的 Spring 应用程序中。 3、验证 List 中的每个元素 假设我们需要验证员工角色列表。每个角色都应以 ROLE_ 开头。 首先在 Employee DTO 类中添加一个 roles 变量: @NotEmpty(message = "Roles list cannot be empty") @Valid private List<@Pattern(regexp = "ROLE_[A-Z]+", message = "Each role must start with 'ROLE_' and contain uppercase letters only") String> roles; @NotEmpty 注解确保列表不是空的,而 @Pattern 注解则验证每个角色都符合指定的格式(正则)。

使用 Java ServerSocket 实现简单的 HTTP 服务器

1、概览 HTTP 服务器通常用于为发起请求的客户端提供资源。Java 中有一系列生产级 Web 服务器。 本文将带你了解如何使用 ServerSocket 类实现一个简单的 Web 服务器,从而了解 HTTP 服务器是如何工作的。注意,此服务器仅用于教学目的,不适合用于生产。 2、ServerSocket 基础 首先,服务器会监听客户端应用程序的连接。客户端应用程序可以是浏览器、其他程序、API 工具等。连接成功后,服务器会响应客户端连接,向客户端提供资源。 ServerSocket 类提供了在指定端口上创建服务器的方法。它使用 accept() 方法监听指定端口上的传入连接。 accept() 方法会阻塞,直到建立连接,并返回一个 Socket 实例。Socket 实例为服务器和客户端之间的通信提供了对输入和输出流的访问。 3、创建 ServerSocket 实例 首先,创建一个监听指定端口的 ServerSocket 对象: int port = 8080; ServerSocket serverSocket = new ServerSocket(port); 接着,使用 accept() 方法接受一个传入连接: while (true) { Socket clientSocket = serverSocket.accept(); // ... } 如上,使用 while 循环等待连接。然后,调用 ServerSocket 对象上的 accept() 方法来监听和接受连接。 连接建立后,该方法会返回一个 Socket 对象,允许服务器和客户端通过已建立的网络进行通信。 4、处理输入和输出 通常,服务器接收来自客户端的输入并发送适当的响应。我们可以使用 Socket 类的 getInputStream() 和 getOutputStream() 方法,通过提供流来读取和写入数据到客户端,从而进行通信。

在 Java 程序中运行 JAR

1、简介 在开发 Java 项目时,我们可能会遇到这样的情况:需要在 Java 程序中启动一个单独的进程运行外部 JAR(可执行 JAR)并查看输出,或者可能想要执行外部 JAR 中带有 main 方法的类文件。 2、运行可执行 JAR 可执行 JAR 是一种 JAR 文件类型,它包含一个设置了 Main-Class 属性的清单(manifest)文件。该属性指向应首先运行(使用 main 方法)的类文件。 我们可以使用 java -jar <example.jar> 命令从命令行运行该 JAR。也可以在 Java 程序中使用 ProcessBuilder 来实现类似的结果。 下面的代码演示了如何以编程式将可执行 JAR 作为单独进程运行,并在控制台中查看输出结果: @Test public void givenRunnableJar_whenExecuted_thenShouldRunSuccessfully() { Process process = null; try { String jarFile = new File(Objects.requireNonNull(getClass().getClassLoader() .getResource(RUNNABLE_JAR_PATH)) .toURI()).getAbsolutePath(); ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", jarFile); processBuilder.redirectErrorStream(true); process = processBuilder.start(); try (InputStream inputStream = process.getInputStream()) { byte[] output = inputStream.