使用 Spring Data JPA 在 PostgreSQL 中存储和检索 JSON 数据,

1、概览 本文将带你全面了解如何使用 Spring Data JPA 在 PostgreSQL JSONB 列中存储、检索 JSON 数据。 2、VARCHAR 映射 本节将介绍如何使用 AttributeConverter 将 VARCHAR 类型的 JSON 值转换为自定义 Java POJO。 其目的是方便 Java 数据类型中的实体属性值与数据库列中的相应值之间的转换。 2.1、Maven 依赖 要创建 AttributeConverter,必须在 pom.xml 中加入 Spring Data JPA 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.7.18</version> </dependency> 2.2、数据表定义 数据库表定义如下: CREATE TABLE student ( student_id VARCHAR(8) PRIMARY KEY, admit_year VARCHAR(4), address VARCHAR(500) ); student 表有三个字段,其中我们希望 address 列能存储具有以下结构的 JSON 值: { "postCode": "TW9 2SF", "city": "London" } 2.3、Entity 类 创建一个相应的 POJO 类,用 Java 表示 address 数据:

Spring Webclient 自定义 JSON 反序列化

1、概览 本文将带你了解如何在 Spring WebClient 中自定义 JSON 的反序列化(Deserialization)。 2、为什么需要自定义反序列化? Spring WebFlux 模块中的 Spring WebClient 通过 Encoder 和 Decoder 组件处理序列化和反序列化。编码器(Encoder)和解码器(Decoder)是表示读取和写入内容的接口。默认情况下,spring-core 模块提供 byte[]、ByteBuffer、DataBuffer、Resource 和 String 编码器和解码器实现。 Jackson 是一个 JSON 库,它通过 ObjectMapper 将 Java 对象序列化为 JSON,并将 JSON 字符串反序列化为 Java 对象。ObjectMapper 包含内置的配置选项,可以使用 Deserialization Feature 进行开启或关闭。 当 Jackson 库提供的默认行为无法满足我们的特定需求时,就有必要定制反序列化过程。为了在序列化和反序列化过程中修改行为,ObjectMapper 提供了一系列配置选项供我们设置。因此,我们需要将这个自定义的 ObjectMapper 注册到 Spring WebClient 中,以便在序列化和反序列化中使用。 3、如何自定义 ObjectMapper? 自定义 ObjectMapper 可以在全局应用程序级别与 WebClient 关联,也可以与特定请求关联。 以一个获取客户订单详细信息的 GET 端点为例。 OrderResponse Model 如下: { "orderId": "a1b2c3d4-e5f6-4a5b-8c9d-0123456789ab", "address": [ "123 Main St", "Apt 456", "Cityville" ], "orderNotes": [ "Special request: Handle with care", "Gift wrapping required" ], "orderDateTime": "2024-01-20T12:34:56" } 对于上述响应的一些反序列化规则如下:

Spring Boot 实现 gPRC 服务调用

1、简介 gRPC 是一个高性能的开源 RPC 框架,最初由谷歌开发。它有助于消除样板代码,并在数据中心内外连接多语言服务。该 API 基于 Protocol Buffer,它提供了一个 protoc 编译器,用于生成不同支持的语言的代码。 我们可以将 gRPC 视为 REST、SOAP 或 GraphQL 的替代品,它建立在 HTTP/2 的基础上,可以使用多路复用或流式连接等功能。 本文将带你了解如何使用 Spring Boot 实现 gRPC 服务提供者和消费者。 2、面临的问题 首先,Spring Boot 并不直接支持 gRPC。它只支持 Protocol Buffer,允许我们实现基于 protobuf 的 REST 服务。因此,需要通过使用第三方库来加入 gRPC: 平台相关的编译器:protoc 编译器是平台相关的。因此,如果在构建过程中需要生成存根(Stub),构建过程会变得更加复杂且容易出错。 依赖: 需要在 Spring Boot 应用中添加兼容的依赖。不幸的是,Java 的 protoc 添加了 javax.annotation.Generated 注解(Issues),这迫使我们需要添加一个依赖项来编译旧的 Java EE 注解库。 服务器运行时:gRPC 服务提供者需要在服务器中运行。gRPC for Java 项目提供了一个 shaded Netty,我们需要将其包含在 Spring Boot 应用中,或者用 Spring Boot 已经提供的服务器来代替。 消息传输: Spring Boot 提供了不同的客户端,如 RestClient(阻塞)或 WebClient(非阻塞),但遗憾的是,这些客户端无法配置和用于 gRPC,因为 gRPC 在阻塞和非阻塞调用中都使用了 自定义 transport 技术。 配置: 由于 gRPC 使用了自己的技术,我们需要配置属性以按照 Spring Boot 的方式对其进行配置。 3、示例项目 幸运的是,可以使用第三方 Spring Boot Starter 来应对这些挑战,例如 LogNet 或 grpc ecosystem project。这两个 Starter 都很容易整合,但后者同时支持提供者和消费者以及许多其他集成功能,因此本文选择了后者。

使用 Spring Modulith 实现事件外部化

1、概览 本文将带你了解如何使用 Spring Modulith 来监听 Spring Application Event 并自动将其发布到 Kafka Topic。 2、事务操作和 Message Broker 假设,正在开发一个保存文章的功能: @Service class Baeldung { private final ArticleRepository articleRepository; // 构造函数 @Transactional public void createArticle(Article article) { validateArticle(article); article = addArticleTags(article); // ...其他业务逻辑 articleRepository.save(article); } } 此外,还需要向系统的其他部分通知这一新文章。其他模块或服务会根据此做出反应,例如创建报告或向网站读者发送新闻邮件。 最简单的方法是使用 KafkaOperations 向 baeldung.articles.published Kafka Topic 发送消息,并使用文章的 slug() 作为关 Key: @Service class Baeldung { private final ArticleRepository articleRepository; private final KafkaOperations<String, ArticlePublishedEvent> messageProducer; // 构造函数 @Transactional public void createArticle(Article article) { // .

从 HttpServletRequest 获取查询参数

1、概览 后端 HTTP API 开发最重要的功能之一是解析前端传递的请求查询参数。 本文将带你了解几种直接从 HttpServletRequest 获取查询参数的方法,以及 Spring MVC 提供的一些简洁方法。 2、HttpServletRequest 中的方法 首先,来看看 HttpServletRequest 提供的与参数相关的方法。 2.1、HttpServletRequest#getQueryString() HttpServletRequest#getQueryString() 可以直接从 URL 获取查询字符串信息: @GetMapping("/api/byGetQueryString") public String byGetQueryString(HttpServletRequest request) { return request.getQueryString(); } 使用 curl 向该 API 发送一个包含多个参数的 GET 请求时,getQueryString() 方法会返回 ? 后面的所有字符: $ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byGetQueryString?username=bob&roles=admin&roles=stuff' username=bob&roles=admin&roles=stuff 如果将 @GetMapping 更改为 @RequestMapping,当使用 POST/PUT/PATCH/DELETE HTTP 方法发送请求时,返回的响应相同。也就是说 HttpServletRequest#getQueryString 始终获取到的是 URL 中的查询参数,无论 HTTP 方法是什么。因此,本教程只关注GET请求。 2.2、HttpServletRequest#getParameter(String) 为了简化参数的解析,HttpServletRequest 提供了一个 getParameter 方法,可以通过参数名获取参数值: @GetMapping("/api/byGetParameter") public String byGetParameter(HttpServletRequest request) { String username = request.

Spring Boot 使用 Redis 实现可靠的消息队列

在应用中把 Redis 当成消息队列来使用已经屡见不鲜了。我想主要原因是当代应用十有八九都会用到 Redis,因此不用再引入其他消息队列系统。而且 Redis 提供了好几种实现消息队列的方法,用起来也简单。 使用 Redis 实现消息队列的几种方式 Redis 提供了多种方式来实现消息队列。 Pub/Sub 订阅发布模式,发布者把消息发布到某个 Channel,该 Channel 的所有订阅者都会收到消息。但是这种方式最大的问题是 发布出去的消息,如果没有被监听消费,或者消费过程中宕机,那么消息就会永远丢失。适合用于临时通知之类的场景,对于需要保证数据不丢失的场景不能使用这种方式。 List List 是 Redis 提供的一种数据类型,底层是链表,可以用来实现队列、栈。 Stream Stream 是一个由 Redis 5 引入的,功能完善的消息队列。想必也是 Redis 官方团队看到太多人拿 Redis 当消息队列使,于是干脆就在 Redis 上设计出一个类似于 Kafka 的消息队列。 Steam 支持消费组消费,一条消息只能被消费组中的其中一个消费者消费。支持 消息确认、支持 回溯消费 还支持把未 ACK(确认)的消息转移给其他消费者进行重新消费,在进行转移的时候还会累计消息的转移次数,当次数达到一定阈值还没消费成功,就可以放入死信队列。 这也是 Redis 种最复杂的一种数据类型。如果你真的到了需要使用 Redis Steam 作为消息队列的地步,那不如直接使用 RabbitMQ 等更加成熟且稳定的消息队列系统。 使用 List 实现可靠的消息队列 目前来说,这是用得最多的一种方式,适用于大多数简单的消息队列应用场景。List 类型有很多指令,但是作为消息队列来说用到的只有几个个: LPUSH key element [element ...] 把元素插入到 List 的首部,如果 List 不存在,会自动创建。 BRPOPLPUSH source destination timeout 移除并且返回 List (source)尾部的最后一个元素,并且同时会把这个元素插入到另一个 List (destination)的首部。

Spring Boot 3.2.2 发布

Spring Boot 3.2.2 版本的详细更新内容如下: 🐞 Bug 修复 SSLBundle 实现未提供有用的 toString() 结果 #39167 JarEntry.getComment() 从 NestedJarFile 实例返回不正确的结果 #39166 在 server.ssl 属性中混合 PEM 和 JKS 类型的证书不起作用 #39158 仅仅在 classpath 上引入 AspectJ 和 Micrometer 并不足以启用对 Micrometer observation 注解的支持 #39128 当映射到 / 时,无法访问未使用选择器(Selector)操作的 Actuator 端点。#39122 由于缺少 Authentication Manager,使用 WebFlux、Security 和 Actuator 的 Spring Boot 3.2 应用可能无法启动 #39096 management.observations.http.server.requests.name 不再生效 #39083 spring.rabbitmq.listener.stream.auto-startup 属性不起作用 #39078 PatternParseException 的日志信息中的错误标记位置不对 #39075 server.jetty.max-connections 配置无效 #39052 依赖于初始的 CharSequence 到 String 转换的 @ConfigurationPropertiesBinding 转换器不再起作用 #39051 在新的加载器实现中无法解析 Manifest attributes #38996 来自日志系统初始化的 Throwable 可能导致应用在启动时默默地失败 #38963 使用 Jetty 时,IO 操作和延迟调度的空闲超时不能设置为小于 30000ms #38960 当 jar 放在 WSL 网络驱动器上时,spring-boot-maven-plugin repackage uber jar 执行失败 #38956 Oracle OJDBC BOM 版本被标记为不适合生产使用 #38943 使用 jOOQ 且未设置 spring.

Spring Cloud Gateway 全局异常处理

1、概览 本文将带你了解如何在 Spring Cloud Gateway 中实现全局异常处理。 在现代软件开发中,尤其是在微服务中,API 的高效管理至关重要。这正是 Spring Cloud Gateway 作为 Spring 生态系统的关键组件发挥重要作用的地方。它就像一个门卫,将流量和请求引导到适当的微服务,并提供跨切面的关注点,如安全性、监控/指标和弹性。 然而,在如此复杂的环境中,由于网络故障、服务宕机或应用错误而产生的异常是必然的,这就需要一个强大的异常处理机制。Spring Cloud Gateway 的全局异常处理可确保在所有服务中采用一致的错误处理方法,并增强整个系统的弹性和可靠性。 2、全局异常处理的必要性 Spring Cloud Gateway 是 Spring 生态系统中的一个项目,旨在作为微服务架构中的 API 网关,其主要作用是根据预先制定的规则将请求路由到相应的微服务。网关提供安全(认证和授权)、监控和熔断(Circuit Breakers)等功能。通过处理请求并将其导向适当的后端服务,它有效地管理了诸如安全性和流量管理等横切关注点。 在微服务等分布式系统中,异常可能来自多个方面,如网络问题、服务不可用、下游服务错误和应用级错误,这些都是常见的罪魁祸首。在这种环境下,以本地化方式(即在每个服务内)处理异常可能会导致零散和不一致的错误处理。这种不一致性会使调试工作变得繁琐,并降低用户体验: 全局异常处理通过提供集中的异常管理机制,确保所有异常(无论其来源如何)都能得到一致的处理,并提供标准化的错误响应,从而应对这一挑战。 这种一致性对于系统恢复能力、简化错误跟踪和分析至关重要。它还能提供精确一致的错误格式,帮助用户了解出错的原因,从而提升用户体验。 3、在 Spring Cloud Gateway 中实现全局异常处理 在 Spring Cloud Gateway 中实现全局异常处理涉及几个关键步骤,每个步骤都能确保建立一个强大而高效的异常管理系统。 3.1、创建自定义全局异常处理器 全局异常处理器对于捕获和处理网关中发生的异常至关重要。为此,需要继承 AbstractErrorWebExceptionHandler 并将其添加到 Spring Context 中。这样,就创建了一个能拦截所有异常的集中式处理器。 @Component public class CustomGlobalExceptionHandler extends AbstractErrorWebExceptionHandler { // 构造函数和其他方法 } 该类应能处理各种类型的异常,从 NullPointerException 等一般异常到 HttpClientErrorException 等更具体的异常。目标是涵盖各种可能的错误。该类的核心方法如下所示。 @Override protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) { return RouterFunctions.

Spring WebFlux Multipart 文件上传

1、概览 Spring WebFlux 是一个响应式 Web 框架,它提供了一个非阻塞 Event Loop,可异步处理 I/O 操作。此外,它还使用 Mono 和 Flux Reactive Stream Publisher 在订阅时发布数据。 这种响应式方式可帮助应用处理大量请求和数据,而无需分配大量资源。 本文将带你了解如何在 Spring WebFlux 中处理 Multipart 文件上传。 2、项目设置 创建一个简单的响应式 Spring Boot 项目,将 Multipart 文件上传到一个目录。 为简单起见,使用项目的根目录来存储文件。在生产中,可以使用 云厂商 OSS、Minio 存储等文件系统。 2.1、Maven 依赖 首先,在 pom.xml 中添加 spring-boot-starter-webflux 依赖,以启动 Spring WebFlux 应用: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>3.2.0</version> </dependency> 它提供核心 Spring WebFlux API 和嵌入式 Netty 服务器,用于构建响应式 Web 应用。 另外,还要在 pom.xml 文件中添加 spring-boot-starter-data-r2dbc 和 H2 数据库依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-r2dbc</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>com.

在 Spring Authorization Server 中将 Authorities 作为自定义 Claim 添加到 JWT Access Token 中。

1、概览 在许多情况下,可以在 JWT Access Token 添加自定义 Claim,从而在 Token Payload 中包含更多信息。 本文将带你了解如何在 Spring Authorization Server 中为 JWT Access Token 添加资源所有者授权。 2、Spring Authorization Server Spring Authorization Server 是 Spring 生态系统中的一个新项目,旨在为 Spring 应用提供授权服务器支持。它通过 Spring 编程模型简化 OAuth 2.0 和 OpenID Connect(OIDC) 授权服务器的实现。 2.1、Maven 依赖 首先,在 pom.xml 中导入 spring-boot-starter-web、spring-boot-starter-security、spring-boot-starter-test 和 spring-security-oauth2-authorization-server 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.5.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.5.4</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-authorization-server</artifactId> <version>0.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>2.5.4</version> </dependency> 或者,可以在 pom.xml 文件中添加 spring-boot-starter-oauth2-authorization-server 依赖: