Spring 和 CORS 跨域

如果你从事 web 应用开发,在前端使用异步请求(fetch/XMLHttpRequest)时,那你或多或少都应该在浏览器控制台见识过如下异常信息。 Access to fetch at 'http://localhost:8080/hello' from origin 'http://localhost:1313' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. 是的,这就是在异步请求时跨域失败的异常信息。接下来本文将会先简单地介绍跨域的基础知识,再详细地介绍如何在 spring 应用中处理跨域。 简单了解跨域 跨域,全称为“跨域资源共享”(Cross-origin resource sharing),是浏览器的一种安全机制。只会在浏览器中,使用 AJAX (fetch/XMLHttpRequest)时发生。当你在一个 web 页面中,使用 ajax 对目标 URL 发起了请求,只要目标 URL 的 协议,主机,端口 和当前 web 页面的 协议,主机,端口 任意不一致,就会产生跨域。 例如,我在页面 http://localhost:1313 中,对如下 URL 发起 AJAX 请求,都会导致跨域:

Spring Boot 的测试教程

在前面的 Spring Boot 入门教程 中,我们学习了如何创建 Spring Boot 应用程序并构建一个简单的 REST API。 在本教程中,你将学习如何为 Spring Boot 应用程序编写单元测试、片段测试和集成测试。 测试 Spring Boot 应用 我们应该编写单元测试来验证特定单元(一个类、一个方法或一组类)的业务逻辑,而且它们不应该与任何外部服务(如数据库、队列或其他网络服务等)有联系。如果我们测试的单元依赖于任何外部服务,那么我们可以提供这些依赖关系的模拟(mock)实现,并验证单元的行为。 除了单元测试外,我们还应该编写集成测试,通过与实际服务和依赖的协作者(dependent collaborator)互动来检验子系统或组件的行为。 当我们生成 Spring Boot 应用程序时,会自动添加 spring-boot-starter-test 依赖项,它可以将 SpringTest、JUnit5、Mockito、Assertj、JsonPath、JsonAssert 等最常用的测试库作为测试依赖项添加到我们的应用程序中。 测试的类型 Unit Tests(单元测试): 这些测试用于验证单个单元的行为,最好不要依赖 Spring 或 Hibernate 等框架。 Slice Tests(片段测试): 这些测试用于验证应用程序的某个片段,如 Web 层或持久层等。Spring Boot 支持使用 @WebMvcTest、@DataJpaTest、@DataMongoTest 等测试应用程序的片段。 Integration Tests(集成测试): 这些测试以黑盒方式测试应用程序。我们传入一些输入,并期待特定的输出,但我们不知道也不关心内部是如何工作的。Spring Boot 支持使用 @SpringBootTest 编写集成测试。 使用 JUnit 5 和 Mockito 进行单元测试 我们将为之前的 Spring Boot 入门教程 中实现的 REST API 编写测试。 让我们从编写 GreetingService 的单元测试开始。我们将使用 JUnit 5 和 Mockito 来编写单元测试。

在 Spring Boot 应用中使用 Loki 记录日志

在本文中,你将学习如何收集 Spring Boot 应用程序日志并将其发送到 Grafana Loki。为此,我们将使用 Loki4j Logback appender 功能。Loki 是一个受 Prometheus 启发的可水平扩展、高度可用的日志聚合系统。我将逐步展示如何配置应用程序与 Loki 之间的集成。不过,你也可以使用我自动配置的用于记录 HTTP 请求和响应的库,它将为你完成所有这些步骤。 源码 如果你想自己尝试,可以克隆我的 GitHub 仓库。点击 此处 查看包含我的自定义 Spring Boot 日志库的源代码仓库。然后按照我的说明操作即可。 使用 Loki4j Logback Appender 为了使用 Loki4j Logback Appender,我们需要在 Maven pom.xml 中加入一个依赖。该库的当前版本为 1.4.1: <dependency> <groupId>com.github.loki4j</groupId> <artifactId>loki-logback-appender</artifactId> <version>1.4.1</version> </dependency> 然后,我们需要在 src/main/resources 目录下创建 logback-spring.xml 文件。我们的 Loki 实例在 http://localhost:3100 地址 (1) 下可用。Loki 不会索引日志内容,只会索引元数据标签。有一些静态标签,如应用程序名称、日志级别或主机名。我们可以在 format.label 字段 (2) 中设置它们。我们还将设置一些动态标签,因此要启用日志回溯标记功能 (3)。最后,我们将设置日志格式模式 (4)。为了简化 LogQL(Loki 查询语言)的潜在转换,我们将使用 JSON 符号。 <?xml version="1.0" encoding="UTF-8"?> <configuration> <springProperty name="name" source="spring.

为什么 JPA Entity 类需要有默认的无参构造函数?

1、概览 在本教程中,我们将学习在 JPA 中,为什么实体类必须要有默认无参数构造函数? 为了理解无参数构造函数的意义,我们将使用一个简单的 Employee 实体示例。我们将观察缺少默认构造函数是如何导致编译时错误的。我们将深入探讨 JPA 在实体实例化中对 Reflection 的使用。此外,我们还将简要介绍这些类中可能需要该构造函数的其他原因。 2、示例 让我们举一个简单的例子,建立一个名为 Employee 的实体类,其中包含 name、department 和自动生成的 id。让我们定义一个包含所有三个字段的构造函数: @Entity public class Employee { @Id private Long id; private String name; private int age; public Employee(Long id, String name, int age) { this.id = id; this.name = name; this.age = age; } // get 和 set } 不过,此时我们会发现 Employee 类无法编译:Class'Employee' should have [public, protected] no-arg constructor。 显然,在这里我们可以看到,我们定义了一个带有全参构造函数的 Entity 类,但却没有无参数构造函数。在这种情况下,会出现一个编译时错误,提示我们除了现有的构造函数外,还需要一个无参数构造函数。 在接下来的两节中,我们将围绕 Entity 类中的构造函数了解 JPA 规范。我们将了解如何修复错误,以及 JPA 施加这些约束的根本原因。

Spring Data JPA 中 findBy 和 findOneBy 的区别

1、概览 Spring Data Repository 提供了大量可简化数据访问逻辑实现的方法。然而,选择合适的方法并不总是像我们想象的那么容易。 以 findBy 和 findOneBy 为前缀的方法就是一个例子。尽管从名称上看,它们似乎做着同样的事情,但其实还是有一些区别的。 2、Spring Data 中的派生查询方法 Spring Data JPA 的派生查询方法功能经常受到称赞。这些方法提供了一种从方法名称派生特定查询的方法。例如,如果我们想通过 foo 属性检索数据,只需写 findByFoo() 即可。 通常,我们可以使用多个前缀来构建派生查询方法。这些前缀包括 findBy 和 findOneBy。下面,我们就来看看它们的实际应用。 3、实例 首先,我们来看看 Person 实体类: @Entity public class Person { @Id private int id; private String firstName; private String lastName; // 标准的 get 和 set 方法 } 在这里,我们将使用 H2 作为数据库。让我们使用一个基本的 SQL 脚本为数据库添加数据: INSERT INTO person (id, first_name, last_name) VALUES(1, 'Azhrioun', 'Abderrahim'); INSERT INTO person (id, first_name, last_name) VALUES(2, 'Brian', 'Wheeler'); INSERT INTO person (id, first_name, last_name) VALUES(3, 'Stella', 'Anderson'); INSERT INTO person (id, first_name, last_name) VALUES(4, 'Stella', 'Wheeler'); 最后,让我们创建一个 JPA repository 来管理我们的 Person 实体:

使用 Webflux R2dbc 和 Postgres 构建响应式 Spring Boot 应用

在本文中,你将学习如何使用 Spring WebFlux、R2DBC 和 Postgres 数据库实现和测试响应式(Reactive) Spring Boot 应用程序。我们将使用最新版本的 Spring Boot 3 创建两个用 Kotlin 编写的简单应用程序。我们的应用程序通过 HTTP 公开一些 REST 端点。为了测试它们之间的通信以及与 Postgres 数据库的集成,我们将使用 Testcontainers 和 Netty Mock Server。 源码 你可以可以克隆我的 GitHub repository。它包含 employee-service 和 organization-service 两个应用程序。之后,你只需按照我的说明操作即可。 依赖 第一步,我们将添加几个与 Kotlin 相关的依赖。除了标准库,我们还可以加入 Kotlin 对 Jackson(JSON 序列化/反序列化)的支持: <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-kotlin</artifactId> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-reflect</artifactId> </dependency> 我们还需要包含两个 Spring Boot Starter。为了创建响应式 Spring @Controller,我们需要使用 Spring WebFlux 模块。有了 Spring Boot Data R2DBC Starter,我们就能以响应方式使用 Spring Data Repository。最后,我们还需要加入 R2DBC 提供的 Postgres 驱动程序。

使用 OpenAPI 生成带有 Lombok 注解的 Model

1、概览 Lombok 是一个 Java 库,有助于减少 getter、setter 等模板代码。OpenAPI 提供了一个属性,用于自动生成带有 Lombok 注解的 Model。 在本教程中,我们将探讨如何使用 OpenAPI 代码生成器生成带有 Lombok 注解的 Model。 2、项目设置 首先,让我们创建一个 Spring Boot 项目,并添加 Spring Boot Starter Web 和 Lombok 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> <scope>provided</scope> </dependency> 此外,我们还需要 Swagger 注解、Gson 和 Java Annotation API 依赖,以防止在生成的代码中出现与包相关的错误: <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.6.2</version> </dependency> 在下一节中,我们将为一个名为 Book 的 model 创建一个 API 规范,然后使用 OpenAPI 代码生成器生成带有 Lombok 注解的代码。

Spring Boot 入门

Spring Boot 介绍 Spring Boot 是 Java 世界中最流行的应用程序构建框架。Spring Boot 是一种基于约定而非配置的方法,用于构建基于 Spring 框架的应用程序。 使用 Spring Boot,你可以构建单体应用程序、微服务、serverless 应用程序、批处理应用程序等不同类型的应用程序。 让我们快速了解一下 Spring Boot 有哪些关键功能使其如此受欢迎。 Spring Boot 关键特性 自动配置 Spring Boot 对应用程序有自己的约定,它会根据默认约定自动配置组件(又称 Bean),而不需要你显式配置所有内容。不过,如果需要,你可以通过各种方式自定义或覆盖 Bean 配置。 例如,如果添加 spring-boot-starter-data-jpa 依赖,它将添加 Hibernate 作为 JPA 实现,因为它是最常用的 JPA 提供程序。此外,Spring Boot 还会自动配置使用 Spring Data Jpa 所需的组件,如 DataSource、EntityManagerFactory、PlatformTransactionManager 等。如果 classpath 中存在内存 JDBC 驱动程序(如 H2 或 HSQL),那么 Spring Boot 将自动配置基于内存的数据源。 如果要使用 MySQL、Postgresql 等非内存数据库,可以添加相应的 JDBC 驱动程序 jar,并在 application.properties 文件中配置 JDBC 连接参数。然后,Spring Boot 将使用这些属性来配置 DataSource Bean,而不是使用默认的内存数据库。你甚至可以使用 @Bean 注解自行配置 DataSource Bean,然后 Spring Boot 将回退并使用你配置的 DataSource Bean,而不是自动配置。

Spring Data JPA 中的 Scroll API

1、概览 Spring Data Commons 是总括 Spring Data project 的一部分,其中包含管理持久层的接口和实现。Scroll API 是 Spring Data Commons 提供的功能之一,用于处理从数据库读取的大型结果。 在本教程中,我们将通过一个示例探索 Scroll API。 2、依赖 Spring Boot 3.1 版本新增了 Scroll API 支持。Spring Data Commons 已包含在 Spring Data JPA 中。因此,添加 Spring Data JPA 3.1 版本就可以获得 Scroll API 功能: <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>3.1.0</version> </dependency> 最新的版本可在 Maven 中央仓库 中找到。 3、 Entity 类 例如,我们将使用 BookReview 实体,它包含不同用户对不同书籍的书评评级: @Entity @Table(name="BOOK_REVIEWS") public class BookReview { @Id @GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "book_reviews_reviews_id_seq") @SequenceGenerator(name = "book_reviews_reviews_id_seq", sequenceName = "book_reviews_reviews_id_seq", allocationSize = 1) private Long reviewsId; private String userId; private String isbn; private String bookRating; // getter 和 setter 方法 } 4、Scroll API Scroll API 提供了分块迭代大型结果的功能。它提供稳定的排序、scroll 类型和结果限制。

如何提高 Maven 的构建速度?

1、概览 在本教程中,我们将学习如何加快 Maven 的构建速度。我们将介绍各种优化构建时间的技术,并评述其优缺点。 2、常规建议 使用正确的 maven phase(阶段) 可以为我们节省大量时间。如果只需要编译代码,就没必要运行完整的 install,并且这会污染我们的本地仓库。 在多模块项目中,可以只重建已更改的模块和依赖于这些模块的模块。例如,如果我们只修改了 module1 和 module2,我们可以运行: $ mvn clean install -pl module1,module2 -am 3、使用多线程 默认情况下,Maven 构建在单线程中顺序运行。不过,如今所有电脑都有多核。让我们利用这一点,使用 -T 选项并行构建我们的模块: $ mvn clean install -T 1C -T 1C 表示 Maven 将在每个可用内核上使用一个线程。 -T 4 会强制 Maven 使用四个线程。 -T auto 会让 Maven 决定使用的线程数。 最后但并非最不重要的一点是,Maven Reactor 可确保所有相互依赖的模块按顺序运行。 4、测试优化 测试是软件开发的重要组成部分。然而,运行测试需要花费大量时间。 4.1、并行运行测试 默认情况下,Surefire 插件会按顺序运行单元测试。不过,我们可以将其配置为并行运行。例如,要并行运行所有测试套件,并在每个可用内核上使用一个线程,我们可以运行: mvn clean install -Dparallel=all -DperCoreThreadCount=true 但是,如果我们的项目中没有大量的单元测试,并行化的开销可能会导致更低的效率。 4.2、跳过测试执行 有时,我们并不需要在本地环境中运行测试。Maven -DskipTests 选项会跳过测试的执行,但仍会编译 test 文件夹: $ mvn clean install -DskipTests 在高度测试的项目中,当我们不需要测试时,跳过测试可以节省我们的时间!