教程

Spring Boot 在运行时启用和禁用端点

1、概览 在 Spring Boot 运行时,出于一些维护目的,我们可能需要动态地启用、禁用某些端点。 在本教程中,我们将学习如何使用 Spring Cloud、Spring Actuator 和 Apache 的 Commons Configuration 等几个常用库在运行时启用和禁用 Spring Boot 端点。 2、项目设置 以下是 Spring Boot 项目的关键设置。 2.1、Maven 依赖 首先,在 pom.xml 文件中添加 spring-boot-starter-actuator 依赖,用于暴露 /refresh 端点: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.7.5</version> </dependency> 接下来,添加 spring-cloud-starter 依赖,因为稍后需要使用其 @RefreshScope 注解来重载 Environment 中的属性源: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter</artifactId> <version>3.1.5</version> </dependency> 还必须在项目 pom.xml 文件的 <dependencyManagement> 中添加 Spring Cloud 的 BOM,以便 Maven 使用兼容版本的 spring-cloud-starter: <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 最后,由于我们需要在运行时重新加载文件的功能,所以还要添加 commons-configuration 依赖:

在 Spring Boot 中使用 Gson 替换 Jackson

GSON 是由谷歌开源的一款 Java JSON 库。特点是轻量,只有一个 JAR,无任何其他依赖。高性能,支持以流式进行序列化/反序列化。并且抽象了基本的 JsonElement、JsonObject、JsonArray、JsonPrimitive 和 JsonNull,可以在无自定义 Java 对象的情况下构建、解析 JSON 对象。 本文将会指导你如何在 Spring Boot 中使用 Gson 代替默认的 Jackson 作为 JSON 序列化/反序列化框架。 添加依赖 在 pom.xml 中添加依赖。 gson 的版本已经被 Spring Boot 管理,所以不需要声明版本号。 <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> 由于 Spring Boot 默认使用 Jackson,所以很多 Spring Boot 的组件、框架默认也会选择用 Jackson 作为 JSON 库。如果你确认项目中,没有其他地方使用到了 Jackson,那么可以从依赖中排除它。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </exclusion> </exclusions> </dependency> 并且,在 @SpringBootApplication 注解中,排除掉 Jackson 的自动装配。 @SpringBootApplication(exclude = { JacksonAutoConfiguration.class }) 配置属性 在 application.

给 RestTemplate 配置 SSL 证书来访问 HTTPS REST 服务

1、概览 在本教程中,我们将学习如何给 RestTemplate 配置 SSL 证书来访问 HTTPS 加密的 REST 服务。 2、设置 要确保 REST 服务的安全,我们需要使用证书和由证书生成的 Keystore。 证书可以从 CA 获取,在本文的示例中,使用的是自签名证书。 我们将使用 Spring 的 RestTemplate 来访问 HTTPS REST 服务。 首先,让我们创建一个 Controller 类 WelcomeController 和一个 /welcome 端点(返回一个简单的字符串响应): @RestController public class WelcomeController { @GetMapping(value = "/welcome") public String welcome() { return "Welcome To Secured REST Service"; } } 然后,在 src/main/resources 文件夹中添加我们的 keystore: src/main/resources |-keystore |-baeldung.p12 接下来,在 application.properties 文件中添加与 Keystore 相关的属性: server.port=8443 server.servlet.context-path=/ # keystore 的格式 server.

Spring Boot 中的枚举(Enum)映射

1、概览 在本教程中,我们将学习如何在 Spring Boot 中实现不区分大小写的枚举映射。 2、Spring 默认的枚举映射 在处理请求参数时,Spring 依靠几个内置 Converter 来处理字符串转换。 通常情况下,将枚举作为请求参数时,默认会使用 StringToEnumConverterFactory 将传递的字符串转换为枚举。 该 Converter 会调用 Enum.valueOf(Class, String),这意味着给定的字符串必须要完全匹配枚举中的常量实例。 例如,让我们来看看 Level 枚举: public enum Level { LOW, MEDIUM, HIGH } 接下来,创建一个使用枚举作为参数的 Handler Method: @RestController @RequestMapping("enummapping") public class EnumMappingController { @GetMapping("/get") public String getByLevel(@RequestParam(required = false) Level level){ return level.name(); } } 使用 CURL 向 http://localhost:8080/enummapping/get?level=MEDIUM 发送一个请求: curl http://localhost:8080/enummapping/get?level=MEDIUM Handler Method 会返回 MEDIUM,即枚举实例 MEDIUM 的名称(name())。 现在,让我们传递 medium,看看会发生什么: curl http://localhost:8080/enummapping/get?level=medium {"timestamp":"2022-11-18T18:41:11.440+00:00","status":400,"error":"Bad Request","path":"/enummapping/get"} 如你所见,返回了无效请求异常:

覆盖 Spring Cloud Config 中的远程属性值

1、概览 Spring Cloud Config 是 Spring Cloud 全家桶的一个子项目。它通过集中式独立的服务来管理各个服务的配置。Spring Cloud Config 拥有自己的属性管理库,但也可以集成 Git、Consul 和 Eureka 等开源项目。 在本文中,我们将了解在 Spring Cloud Config 中覆盖远程属性值的不同方法,以及 Spring 从 2.4 版本开始强制实施的限制,以及 3.0 版本中的变化。在本教程中,我们使用 Spring Boot 2.7.2。 2、创建 Spring Config Server 使用 Spring Cloud Config 创建外部化的配置服务器。 2.1、创建配置文件 在 application.properties 文件中定义的配置与所有客户端应用共享。也可以为指定应用或指定 Profile 定义特定配置。 首先,创建一个配置文件,其中包含为客户端应用提供的属性。 客户端应用命名为 baeldung,在 /resources/config 文件夹中创建 baeldung.properties 文件。 2.2、添加属性 在 baeldung.properties 文件中添加一些属性,然后在客户端应用中使用这些属性: hello=Hello Jane Doe! welcome=Welcome Jane Doe! 还要在 resources/config/application.properties 文件中添加一个共享属性,Spring 将在所有客户端中共享该属性: shared-property=This property is shared accross all client applications 2.

Spring 中的 FeignClient 与 WebClient

1、概览 在本教程中,我们将对 Spring Feign(声明式 REST 客户端)和 Spring WebClient(Spring 5 中引入的响应式 Web 客户端)的优缺点进行比较。 2、阻塞式和非阻塞式客户端 在微服务系统中,服务之间的通常通过 RESTful API 进行通信。因此,需要一个 Web 客户端来执行请求。 接下来,我们将了解阻塞式 Feign 客户端与非阻塞式 WebClient 之间的区别。 2.1、阻塞式 Feign 客户端 Feign 客户端是一种声明式 REST 客户端,能快速、简单地开发 Web 客户端。使用 Feign 时,只需定义接口并对其进行相应注解。然后,Spring 会在运行时生成实际的 Web 客户端实现。 @FeignClient 的实现使用 “每个请求一个线程” 的同步模型。因此,对于每个请求,当前线程会阻塞,直到收到响应为止。如果同时发起多个请求,就要使用大量的线程,每个打开的线程都会占用内存和CPU。 2.2、非阻塞式 WebClient 客户端 WebClient 是 Spring WebFlux 的一部分。它是 Spring Reactive Framework 提供的一种非阻塞解决方案,用于解决 Feign 客户端等同步实现的性能瓶颈。 Feign 客户端会为每个请求创建一个线程并阻塞它,直到收到响应为止,而 WebClient 会执行 HTTP 请求并将 “等待响应” 任务添加到队列中。随后,在收到响应后,从队列中执行 “等待响应” 任务,最后将响应发送给 Subscriber (订阅者)函数。 Reactive 框架实现了由 Reactive Streams API 支持的事件驱动架构。正如我们所看到的,这使我们能够编写只需要最少数量的阻塞线程就能执行 HTTP 请求的服务。

JdbcTemplate 获取自增ID

JdbcTemplate 是由 spring-jdbc 提供的一个 JDBC 工具类模块,可以快速地对数据库进行 CURD 操作。 本文将会教你如何在使用 JdbcTemplate 执行 INSERT 操作时获取到自增ID。 项目设置 创建任意 spring boot 项目。添加 mysql-connector-j 以及 spring-boot-starter-jdbc 依赖。 你可以通过 start.springboot.io 快速创建此示例项目。 <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> 配置数据源 在 application.yaml 中配置基础的数据源信息。 spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true username: root password: root 配置 JdbcTemplate 通过配置类,注册 JdbcTemplate Bean。 import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; @Configuration public class JdbcTemplateConfiguration { @Bean public JdbcTemplate jdbcTemplate (DataSource dataSource) { return new JdbcTemplate(dataSource); } } 创建示例表 CREATE TABLE `users` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `account` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统用户'; 其中 id 字段是自增的。

在 Spring Context 中重新初始化 Singleton Bean

1、概览 在本教程中,我们将了解如何在运行时重新初始化 Spring Context 中的 Singleton Bean。 默认情况下,Singleton Scope Bean 不会在应用生命周期中重新初始化。不过,有时可能需要重新创建 Bean,例如在需要更新属性时。我们将介绍几种实现此目的的方法。 2、示例 一个小示例。创建一个 Bean,从配置文件中读取属性保存在内存中。如果配置文件中的属性值被修改,那么 Bean 就要重新加载配置。 2.1、Singleton Bean 首先创建 ConfigManager 类: @Service("ConfigManager") public class ConfigManager { private static final Log LOG = LogFactory.getLog(ConfigManager.class); private Map<String, Object> config; private final String filePath; public ConfigManager(@Value("${config.file.path}") String filePath) { this.filePath = filePath; initConfigs(); } private void initConfigs() { Properties properties = new Properties(); try { properties.load(Files.newInputStream(Paths.get(filePath))); } catch (IOException e) { LOG.error("Error loading configuration:", e); } config = new HashMap<>(); for (Map.

Spring Cloud Gateway 根据客户端 IP 限制请求速率

1、简介 在本教程中,我们将学习如何在 Spring Cloud Gateway 中根据客户端的实际 IP 地址来限制请求速率。 简而言之,我们将在路由上设置 RequestRateLimiter Filter,然后配置网关根据 IP 地址来限制客户端的请求。 2、路由配置 首先,我们需要配置 Spring Cloud Gateway 以对特定路由进行速率限制。为此,我们将使用由 spring-boot-starter-data-redis-reactive 实现的经典 令牌桶(Token Bucket) Rate Limiter。简而言之, Rate Limiter 创建一个带有唯一 Key 的 Bucket,该 Key 用于标识 Bucket 自身,并具有固定的初始 Token 容量,随时间自动生成 Token。然后,对于每个请求,Rate Limiter 会获取其关联的 Bucket,并试图减少其 Bucket 中的一个 Token。如果 Bucket 中的 Token 数量不足,将拒绝传入的请求。 在分布式系统中,我们希望所有系统对同一客户端的请求都采用相同的限速策略。所以,我们使用分布式缓存 Redis 来存储 Bucket。在本例中,我们预先配置了一个 Redis 实例。 接下来,配置一个带有 Rate Limiter 的路由。监听 /example 端点,并将请求转发至 http://example.org: @Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { return builder.routes() .route("requestratelimiter_route", p -> p .

在 HttpServletRequest 中添加自定义请求头

HTTP 请求头一般都是由客户端设置,服务端获取,用以处理业务。但是偶尔也有一些需求,需要服务端对一些请求,添加自定义的请求头。例如:统一为所有请求添加一个名为 X-Requested-Id 的请求头,用于跟踪不同请求。 Servlet 中的请求对象 jakarta.servlet.http.HttpServletRequest 并未提供设置 Header 的方法。 但是,但是通过一些方法,可以实现。本教程将会教你如何在 Spring Boot 应用中添加自定义请求头到 HttpServletRequest。 思路 既然可以从 HttpServletRequest 获取到 Header,那它一定是把 Header 存储在了某个地方。我们只要找到 HttpServletRequest 存储 Header 的容器,就可以对其进行添加、编辑、删除操作。 不同 HttpServletRequest 的实现中,对 Header 的存储方式不一定一样。目前,在 Spring Boot 中最流行的 Servlet 实现就是 Tomcat 和 Undertow,所以下文将会针对这两个服务器进行讲解。 示例应用 创建一个 Spring Boot 应用。我们要在 Filter 中为所有请求添加一个 X-Requested-Id 请求头,并在 Controller 中获取并返回。 Controller 如下: import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import jakarta.servlet.http.HttpServletRequest; @RestController @RequestMapping("/demo") public class DemoController { @GetMapping public String getHeaders (HttpServletRequest request) { // 获取 X-Requested-Id Header 值 String requestedId = request.