Spring-Boot

Spring Boot 自动配置的原理

1、什么是 Spring Boot 自动配置 首先介绍一下什么是 Spring Boot,Spring Boost 是基于 Spring 框架开发出来的功能更强大的 Java 程序开发框架,其最主要的特点是:能使程序开发者快速搭建一套开发环境。Spring Boot 能将主流的开发框架(例如 SpringMVC、Dubbo、Mybatis、Redis 等),做到像 Maven 导入 Jar 包一样的简洁快速,做到开箱即用。其中最关键的技术就是 Spring Boot 定制的各种 Starter,通 Maven 引入 Starter 就能快速搭建开发环境。 2、SpringBoot Starter自动装配案例 在以前单独使用 SpringMVC Web 编程框架时,我们需要单独配置 DispatcherServlet 和 Tomcat,使用 Spring Boot 之后,我们只需要引入 spring-boot-Starter-web 就能直接开始编写 Controller 等 Web 相关的代码,这就是 Spring Boot 为们提供的开箱即用的便捷能力,下面就以 spring-boot-Starter-web 来说明 Spring Boot自动配置的关键原理。 3、SpringBoot 自动装配案例源码解析 3.1、DispatcherServlet 的自动配置原理 首先我们定位到 Spring Boot 自动配置的 Maven 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>${spring-boot.version}</version> </dependency> 在依赖的 Jar 包中我们可以在 META-INF/spring.

Spring Data MongoDB 开启 Debug 日志

1、概览 在使用 Spring Data MongoDB 时,可能需要比默认级别(INFO)更高的日志,以查看执行语句或查询参数等一些附加信息。 2、配置 MongoDB 查询日志 Spring Data MongoDB 通过 MongoOperations 接口或其主要的实现 MongoTemplate 来访问数据,因此只需为 MongoTemplate 类配置调 Debug 级别的日志即可。 与其他 Spring 或 Java 应用一样,可以使用日志库并为 MongoTemplate 定义日志级别。类似于如下: <logger name="org.springframework.data.mongodb.core.MongoTemplate" level="DEBUG" /> 如果是 Spring Boot 应用,可以直接在 application.properties 文件中进行配置: logging.level.org.springframework.data.mongodb.core.MongoTemplate=DEBUG 同样,也可以使用 YAML 文件: logging: level: org: springframework: data: mongodb: core: MongoTemplate: DEBUG 3、日志测试类 创建一个 Book 类: @Document(collection = "book") public class Book { @MongoId private ObjectId id; private String bookName; private String authorName; // getter 和 setter 方法 } 创建一个简单的测试类用于测试,并查看输出日志。

在 Spring Boot 中使用 DataFieldMaxValueIncrementer 获取自增序列

DataFieldMaxValueIncrementer 是 spring-jdbc 项目中的一个接口。用于在应用中生成连续、自增的序列。可用于 主键ID、订单号、流水号 等等。 它基于数据库实现,主要有 2 大子类。 AbstractSequenceMaxValueIncrementer:用于支持序列(SEQUENCE)的数据库(如 Oracle),使用标准的数据库序列。 AbstractColumnMaxValueIncrementer:用于不支持序列的数据库(如,MYSQL),使用一张表来模拟。 spring-jdbc 已经为当前流行的关系型数据库提供了具体的实现,类体系结构如下: DataFieldMaxValueIncrementer |-AbstractDataFieldMaxValueIncrementer |-AbstractSequenceMaxValueIncrementer |-OracleSequenceMaxValueIncrementer # Oracle 数据库 |-PostgresSequenceMaxValueIncrementer # Postgres 数据库 |-SqlServerSequenceMaxValueIncrementer # SqlServer 数据库 |- ... # 还有一些其他的,这里忽略 |-AbstractColumnMaxValueIncrementer |-MySQLMaxValueIncrementer # MYSQL 数据库 DataFieldMaxValueIncrementer DataFieldMaxValueIncrementer 接口只有 3 个方法,很简单。 public interface DataFieldMaxValueIncrementer { int nextIntValue() throws DataAccessException; long nextLongValue() throws DataAccessException; String nextStringValue() throws DataAccessException; } nextIntValue:以 int 类型返回下一个序列。 nextLongValue:以 long 类型返回下一个序列。 nextStringValue:以 String 类型返回下一个序列。 使用 MySQLMaxValueIncrementer 本文以 MySQLMaxValueIncrementer 为例(毕竟 MYSQL 最流行)。

Spring Boot 属性迁移

1、简介 本文将带你了解 spring-boot-properties-migrator 模块,它是 Spring 为促进 Spring Boot 升级而提供的支持。用于帮助迁移 application properties。 随着每个 Spring Boot 版本的升级,可能会有一些属性被标记为已弃用、不再支持或新引入的属性。Spring 为每个升级发布了详细的 变更日志。然而,阅读这些变更日志可能会有些繁琐。这就该 spring-boot-properties-migrator 出场了,它通过为我们的设置提供个性化的信息来帮助我们进行属性迁移。 让我们来看看如何使用。 2、Demo 应用 把 Spring Boot 应用从 2.3.0 升级到 2.6.3。 2.1、Properties 在演示应用中,有两个 properties 文件。在默认 properties 文件 application.properties 中,添加如下配置: spring.resources.cache.period=31536000 spring.resources.chain.compressed=false spring.resources.chain.html-application-cache=false dev Profile YAML 文件 application-dev.yaml: spring: resources: cache: period: 31536000 chain: compressed: true html-application-cache: true properties 文件包含了几个在 Spring Boot 2.3.0 和 2.6.3 之间被替换或移除的属性。 同时提供了 .properties 和 .yaml 文件,以便更好地进行演示。 2.2、添加依赖 添加 spring-boot-properties-migrator 模块。

Spring Boot 下载文件名乱码

对于文件的下载功能来说,我归纳为 2 头 1 流。极其简单。 Content-Type 头,告诉客户端文件类型。 Content-Disposition 头,告诉客户端对于这个文件的处理方式(在浏览器中显示,还是下载)。 Output 流,写入文件内容到客户端。 文件名称乱码的问题 Content-Disposition 头有三个属性值,可以指定文件的名称。 name filename filename* 至于区别,我也说不大清楚。反正是有个规范,我感觉很乱。具体可以参考如下链接: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition 这就是容易出问题的地方了,由于文件名称设置方法不正确。或者是浏览器不兼容问题,如果 文件名称包含中文,可能会导致 下载文件名称乱码: response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "中文の文件.txt"); 如上,直接用 + 拼接中文字符串,在谷歌浏览器中下载就是乱码。 常见的解决方案 以下我是在网上看到的一些解决方案。 对文件名称进行URI编码 response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode("中文の文件.txt", StandardCharsets.UTF_8)); 修改字符编码 response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + new String("中文の文件.txt".getBytes("GBK"),"ISO-8859-1")); 经过测试,上面2种办法都OK。 比较优雅的方式 Spring 提供了一个工具类: org.springframework.http.ContentDisposition,专门用于处理文件下载中的 Content-Disposition Header,它会自动处理好中文文件名称的编码问题。 ContentDisposition 使用实在是太简单了,这里就不多说了,给一个完整的使用示例就能看明白。 package io.springboot.demo.web.controller; import java.io.IOException; import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletResponse; import org.springframework.http.ContentDisposition; import org.springframework.http.HttpHeaders; import org.springframework.web.bind.annotation.GetMapping; import org.

在 Docker 中设置 Spring Boot Profile

1、简介 在容器化大行其道的今天,越来越多人选择把 Spring Boot 应用部署到 Docker 中。 本文将带你了解在 Docker 容器中启动 Spring Boot 应用时,如何设置其 Profile。 2、基本的 Dockerfile 一般来说,要容器化 Spring Boot 应用,只需提供一个 Dockerfile。 Spring Boot 应用的最小 Dockerfile,如下: FROM openjdk:11 COPY target/*.jar app.jar ENTRYPOINT ["java", "-jar", "/app.jar"] 然后,可以通过 docker build 来构建 Docker 镜像: docker build --tag=docker-with-spring-profile:latest . 接着,我们就可以通过 docker-with-spring-profile 镜像运行应用了: docker run docker-with-spring-profile:latest 你可以注意到,Spring Boot 应用以 “default” Profile 启动: 2022-04-22 22:34:25.268 INFO 1 --- [main] c.b.docker.spring.DemoApplication: Starting DemoApplication using Java 11.0.14.1 on ea8851bea75f with PID 1 (/app.

Spring Boot 3.1 中的 ConnectionDetails

1、概览 本文将带你了解 Spring Boot 3.1 中引入的 ConnectionDetails 接口,用于编程式定义连接属性(可从外部服务中加载)。Spring Boot 提供了开箱即用的抽象,用于与远程服务集成,如关系型数据库、NoSQL 数据库、消息队列服务等 传统上,连接信息都是配置在 application.properties 文件中的。因此,如果需要从 AWS Secret Manager、Hashicorp Vault 等外部服务加载连接配置的话就比较麻烦。 为了解决这个问题,Spring Boot 引入了 ConnectionDetails。这个接口是空的,即标记接口。Spring 提供了该接口的子接口,如 JdbcConnectionDetails、CassandraConnectionDetails、KafkaConnectionDetails 等。它们可以在 Spring 配置类中实现和指定为 Bean。之后,Spring 将依赖这些配置 Bean 来动态检索连接属性,而不是静态的 application.properties 文件。 2、场景 把敏感的配置信息(如数据库)定义在 application.properties 文件中存在一定的安全隐患。最好的方式是使用专业的私密信息存储服务。 HashiCorp Vault 是一款企业级私密信息管理工具,用于安全地存储和访问敏感数据,例如API密钥、数据库凭据、密码等。支持多种认证方法和加密机制,并提供 API 和命令行界面,以便与应用程序和工具集成。还支持动态秘密生成、密钥轮换和审计日志等功能,使数据的管理和安全性更加便捷和可靠。 Spring Boot 从 Vault 中读取敏感信息的流程如下: Spring Boot 通过 Secret Key 调用 Vault 服务来获取连接配置信息。然后,使用该配置和远程服务创建连接。 4、ConnectionDetails 通过 ConnectionDetails 接口,Spring Boot 应用可以自行发现连接细节(Connection Detail),而无需任何手动干预。注意,ConnectionDetails 优先于 application.properties 文件。不过,仍有一些非连接属性(如 JDBC 连接池大小)可以通过 application.properties 文件进行配置。

在 Spring Boot 中通过 ResponseBodyAdvice 统一编码响应体

在前文 《在 Spring Boot 中通过 RequestBodyAdvice 统一解码请求体》 中,我们介绍了如何通过 RequestBodyAdvice 组件统一地解码客户端编码后的请求体。 有请求,就有响应,本文将会带你了解如何使用 ResponseBodyAdvice 来统一对响应体进行编码。 这种场景比较常见,常用于 APP 客户端和服务器之间的通信。为了避免中间人通过抓包直接获取到服务器的响应数据,所以会对数据进行加密(AES、3DES、RSA 等等)。 客户端获取到加密数据后,使用本地存储的密钥进行解密从而得到原始数据。由于密钥没有经过网络传输,意味着加密数据不会轻易被人破解。当然,密钥由于存储在客户端,需要通过混淆等手段保证它不会被轻易的破解、获取(这种安全话题超出了本文的范畴)! ResponseBodyAdvice ResponseBodyAdvice spring mvc 提供的一个增强接口,用于在返回对象被 HttpMessageConverter 执行序列化之前对其进行一些自定义的操作。 public interface ResponseBodyAdvice <T> { boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType); @Nullable T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response); } 这是一个带泛型 <T> 的接口,T 表示返回的对象类型,这决定了它会对哪些返回的对象生效。另外,它比 RequestBodyAdvice 简单,它只有 2 个方法。 supports:用于确定该实现类是否支持对响应体进行处理,通过 returnType 参数可以获取到 Controller 方法的返回类型等信息。 beforeBodyWrite:该方法在响应体写入之前被调用,在这个方法中可以通过参数获取到最终要响应给客户端的对象,我们可以对这个对象进行一些操作,最后返回修改后的对象。 接下来我们通过一个示例来了解如何使用 ResponseBodyAdvice。

在 Spring Boot 中通过 RequestBodyAdvice 统一解码请求体

在一些数据比较敏感或者对安全要求比较高的应用中,客户端提交给服务器的数据需要进行加密,服务器需要解密后才能获取到原始的请求数据。 在 Spring Boot 中,可以通过 RequestBodyAdvice 对请求体进行统一的解密处理,这对 Controller 来说是完全透明的,极大地提高了应用的可维护性。 RequestBodyAdvice 接口 这是由 spring mvc 提供的一个增强接口,用于在 HttpMessageConverter 读取请求体并把它转换为 Java 对象之前进行一些操作。 public interface RequestBodyAdvice { boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType); HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException; Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType); @Nullable Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?

Spring Boot API 的版本控制

简介 本文将带你了解 Spring Boot 应用中对 API 进行版本控制的重要性,以及不同的实现方式。 通过 API 版本控制,你可以对API进行更改,而不会破坏与现有客户端的兼容性。本文将介绍四种常见的版本控制方式:URI 版本控制、请求参数版本控制、自定义 Header 版本控制和内容协商(Accept Header)版本控制。 URI 版本控制 URI 版本控制涉及将版本号添加到 API 的 URL 中。这是一种直接而易于理解的方法。然而,它可能不是最优雅的方法,因为它可能导致URL变得很长。 示例: @RestController @RequestMapping("/api/v1/users") public class UserControllerV1 { @GetMapping public ResponseEntity<List<User>> getUsers() { // 实现代码 } // 其他端点 } 请求参数版本控制 采用这种方法时,版本号会作为请求参数传递。这种方法比 URI 版本控制更省事,但对消费者来说可能不那么直观。 示例: @RestController public class UserController { @GetMapping(value = "/api/users", params = "version=1") public ResponseEntity<List<User>> getUsersV1() { // 实现代码 } @GetMapping(value = "/api/users", params = "version=2") public ResponseEntity<List<User>> getUsersV2() { // 实现代码 } } 自定义 Header 版本控制 自定义 Header 版本控制涉及向 HTTP 请求添加一个自定义 Header,其中包含版本号。这种方法可以保持 URI 的清晰,但消费者必须记住要添加自定义 Header。