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.
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 方法 } 创建一个简单的测试类用于测试,并查看输出日志。
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 最流行)。
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 模块。
对于文件的下载功能来说,我归纳为 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.
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.
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 中通过 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 对请求体进行统一的解密处理,这对 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 进行版本控制的重要性,以及不同的实现方式。
通过 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。