在 Spring Boot 中自定义 Date/LocalDateTime/LocalDate 的序列化、反序列化格式

Spring Boot 中默认使用的 JSON 框架是 jackson,它负责把 JSON 请求体反序列化为 Java 对象,并把响应给客户端的 Java 对象序列化为 JSON 字符串。 本文将会详细介绍如何在 Spring Boot 应用中自定义 jackson 对 Date、LocalDateTime、LocalDate 等日期对象的序列化、反序列化格式。 Jackson 对 Date/LocalDateTime/LocalDate 的默认处理方式 为了处理 java.time 类型的日期类,你还需要在项目中添加 com.fasterxml.jackson.datatype:jackson-datatype-jsr310 模块。 <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency> 默认情况下,Jackson 将 Date 对象序列化为时间戳。对于 LocalDateTime 和 LocalDate 对象,jackson 会序列化为一个 long[],其中,从第一个元素开始分别表示日期的:年、月、日、时、分、秒、毫秒。 示例: package cn.springdoc.test; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; public class JacksonTest { public static void main(String[] args) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); // 注册 JavaTime 模块 objectMapper.

Spring 中的事务管理器(TransactionManager)

事务管理 ,一个被说烂的也被看烂的话题,还是面试中常问到的问题之一。 本文会从设计角度,一步步的剖析 Spring 事务管理的设计思路(都会设计事务管理器了,还能玩不转?)。 一、为什么需要事务管理? 先看看如果没有事务管理器的话,如果想让多个操作(方法/类)处在一个事务里应该怎么做: // MethodA: public void methodA(){ Connection connection = acquireConnection(); try{ int updated = connection.prepareStatement().executeUpdate(); methodB(connection); connection.commit(); }catch (Exception e){ rollback(connection); }finally { releaseConnection(connection); } } // MethodB: public void methodB(Connection connection){ int updated = connection.prepareStatement().executeUpdate(); } 或者用 ThreadLocal 存储 Connection? static ThreadLocal<Connection> connHolder = new ThreadLocal<>(); // MethodA: public void methodA(){ Connection connection = acquireConnection(); connHolder.set(connection); try{ int updated = connection.prepareStatement().executeUpdate(); methodB(); connection.commit(); }catch (Exception e){ rollback(connection); }finally { releaseConnection(connection); connHolder.

使用 Spring Doc 为 Spring REST API 生成 OpenAPI 3.0 文档

1、概览 文档是构建 REST API 的重要组成部分。在本教程中,我们将介绍 Spring Doc,它可简化 API 文档的生成和维护,这些文档基于 OpenAPI 3 规范,适用于 Spring Boot 3.x 应用程序。 2、设置 springdoc-openapi Spring Boot 3.x 要求使用 springdoc-openapi version 2: <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.1.0</version> </dependency> 2.1、 OpenAPI 描述的路径 正确设置依赖后,我们就可以运行应用程序,并在 /v3/api-docs 路径访问 OpenAPI 描述,这是默认路径: http://localhost:8080/v3/api-docs 此外,我们还可以使用 springdoc.api-docs 属性在 application.properties 中自定义描述的路径。例如,我们可以将路径设置为 /api-docs: springdoc.api-docs.path=/api-docs 然后,我们就可以通过以下网址访问文档描述: http://localhost:8080/api-docs OpenAPI 描述定义默认为 JSON 格式。对于 yaml 格式,我们可以从以下网址获取定义: http://localhost:8080/api-docs.yaml 3、整合 Swagger UI 除了生成 OpenAPI 3 规范之外,我们还可以将 springdoc-openapi 与 Swagger UI 集成,以与我们的 API 规范进行交互并测试端点。 Springdoc-openapi 依赖项中已经包含了 Swagger UI,因此我们可以通过如下路径访问 API 文档:

在 Spring Boot 应用中使用 Grpc 进行通信

在本文中,你将学习如何在 Spring Boot 应用程序中实现使用 gRPC 进行通信。gRPC 是一个现代的开源远程过程调用(RPC)框架,可以在任何环境中运行。默认情况下,它使用 Google 的 Protocol Buffer 来对结构化数据进行序列化和反序列化。当然,我们也可以切换到其他数据格式,比如 JSON。为了简化我们在 gRPC 和 Spring Boot 中的使用体验,我们将使用一个专门的 starter。由于没有官方支持的 gRPC 和 Spring Boot 集成的 starter,我们将选择较流行的第三方项目 - grpc-spring-boot-starter。该项目在 GitHub 上有大约 3.1k 个星星。你可以在 这里 找到关于其功能的详细文档。 源码 如果你想亲自尝试,你可以克隆我的 GitHub 仓库。它包含四个应用程序。其中 account-service 和 customer-service 与我之前的文章相关,介绍了 Java 中的 Protocol Buffers。对于这篇文章,请参考另外两个应用 account-service-grpc 和 customer-service-grpc。它们与对应的前2个应用非常相似,但使用了我们的第三方 Spring Boot 和 gRPC 通信,而不是 REST。此外,它们需要使用 Spring Boot 2,因为我们的第三方 starter 还不支持 Spring Boot 3。无论如何,克隆了仓库后,只需按照我的说明进行操作即可。 生成 gRPC 的 Model 类和 service 第一步,我们将使用 .proto 文件生成 model 类和 gRPC service。我们需要加入一些额外的 Protobuf schema,以便使用 google.

Spring Boot 整合 MyBatis

MyBatis 是一个流行的 Java 持久层框架,它简化了与关系型数据库的交互。通过将 SQL 语句与 Java 代码进行映射,MyBatis 提供了一种方便、灵活的方式来执行数据库操作。它支持动态SQL、缓存机制和插件扩展,使得开发人员能够更高效地编写和管理数据库访问代码。作为一种轻量级框架,MyBatis 在 Java 开发中被广泛应用于构建可靠的持久化解决方案。 本文将会指导你如何在 Spring Boot 中整合 MyBatis。 框架版本: Spring Boot:3.1.3 MyBatis:3.5.13 创建 Spring Boot 项目 通过 start.springboot.io 创建工程(你可以直接点击 这个链接 快速创建)。 选择 MySQL Driver、Spring Web、MyBatis Framework 基本依赖,点击 “GENERATE” 下载到本地后,导入到IDEA中。 本文将在示例中使用 MySQL 数据库,如果你使用其他类型的数据库,需要把 MySQL Driver 替换为对应的依赖。 配置项目 定义 mapper 接口 创建专门存放 mapper 接口的包: cn.springdoc.mapper,并在其中定义一个 FooMapper,如下: package cn.springdoc.mapper; import java.time.LocalDateTime; import org.apache.ibatis.annotations.Mapper; @Mapper // 使用 Mapper 注解 public interface FooMapper { /** * 获取数据库的当前时间 * @return */ LocalDateTime now(); } 该 Mapper 简单定义了一个 now 用于从数据库获取到当前时间。

在 Spring 应用中处理 CORS 跨域

1、概览 在任何现代浏览器中,随着通过 REST API 获取数据的 HTML5 和 JS 客户端的出现,跨源资源共享 (CORS) 已成为一种相关规范。 通常,提供 JS 的主机(如 example.com)与提供数据的主机(如 api.example.com)不同。在这种情况下,CORS 可以实现跨域通信。 Spring 为 CORS 提供了一流的 支持,为在任何 Spring 或 Spring Boot Web 应用中配置 CORS 提供了简单而强大的方法。 2、Controller 方法级的 CORS 配置 启用 CORS 非常简单,只需添加注解 @CrossOrigin。 我们可以通过几种不同的方式来实现。 2.1、在 @RequestMapping 方法上注解 @CrossOrigin @RestController @RequestMapping("/account") public class AccountController { @CrossOrigin @RequestMapping(method = RequestMethod.GET, path = "/{id}") public Account retrieve(@PathVariable Long id) { // ... } @RequestMapping(method = RequestMethod.DELETE, path = "/{id}") public void remove(@PathVariable Long id) { // .

在 Spring Boot 应用中把上传的图片编码为 WEBP 格式

WebP 是一种现代的图像格式,由 Google 开发。它采用了无损和有损的压缩算法,可以显著减小图像文件的大小,同时保持较高的视觉质量。WebP 图像通常比 JPEG 和 PNG 格式的图像更小,并且具有更快的加载速度,这对于 Web 应用程序和网页的性能优化非常有益。此外,WebP 还支持透明度和动画,使其成为一个多功能的图像格式。它已经得到了广泛的支持,包括主流的 Web 浏览器和图像处理软件。 简单理解就是:Webp编码格式的图片,体积更小,质量不减(肉眼很难看出质量差异),主流浏览器都支持。 据说使用 webp 编码的图片,有利于搜索引擎 SEO。 参考资料: Webp 官网 浏览器兼容情况 在 Java 中编码 Webp 图片 WEBP 官方开放了源码,以及编译后的可执行文件(可以通过命令行的形式对图片文件进行编码,解码处理),官方并未提供 Java 的 SDK。 我翻遍了互联网,在网上找到了一个开源的 webp 编码库:https://github.com/sejda-pdf/webp-imageio <!-- https://mvnrepository.com/artifact/org.sejda.imageio/webp-imageio --> <dependency> <groupId>org.sejda.imageio</groupId> <artifactId>webp-imageio</artifactId> <version>0.1.6</version> </dependency> 它貌似采用了 JNI 技术来调用 webp 的动态库来实现的编码。使用方式及其简单,如下: import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.FileImageOutputStream; import com.luciad.imageio.webp.WebPWriteParam; public class Webp { /** * 编码为WEBP * * @param in 输入文件 * @param file 输出文件 * @throws IOException */ public void encode(InputStream in, File file) throws IOException { // 读取图片文件 BufferedImage image = ImageIO.

Spring Boot 配置教程

在上一篇 Spring Boot 测试教程 中,我们学习了如何为 Spring Boot 应用编写单元测试、片段测试和集成测试。 在本教程中,你将学习如何使用 properties 和 YAML 文件配置 Spring Boot 应用程序,以便在不同环境中运行应用。 外部化 Spring Boot 的配置 大多数应用程序都会有一些配置项目,你希望这些项目是可以灵活配置的,而不是在程序代码中硬编码这些值。 Spring Boot 提供了许多 配置 application properties 的方法,其中我们最有可能使用的只有以下方法: 默认 src/main/resources/application.{properties/yml} 中的属性值。 特定 profile src/main/resources/application-{profile}.{properties/yml} 中的属性值。 使用环境变量(Environment Variables)或系统属性(System Properties)覆盖默认配置。 在我们的应用程序中,通常会有两种配置属性: Spring Boot 定义了用于配置 DataSource、Kafka 等服务的属性。例如:spring.datasource.url、spring.datasource.username、spring.datasource.password 等。 应用特定的配置属性。 配置默认属性 Spring Boot 默认从 src/main/resources/application.{properties/yml} 中加载属性。例如,我们可以使用 application.properties 文件配置应用属性,如下所示: spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=postgres spring.datasource.password=secret123 ftp.host=ftpsrv001 ftp.port=21 ftp.username=appuser1 ftp.password=secret321 同样的配置值也可以使用 application.yml 文件进行配置,如下所示: spring: datasource: url: jdbc:postgresql://localhost:5432/postgres username: postgres password: secret123 ftp: host: ftpsrv001 port: 21 username: appuser1 password: secret321 配置特定于 Profile 的属性 我们的应用可能会在不同的环境中运行,如本地、开发、QA、暂存和生产环境。我们可能希望为不同的环境配置不同的值。在这种情况下,我们可以使用 Spring 的 profile 概念来为不同环境配置不同的值。

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 来编写单元测试。