JdbcClient 返回自增 ID

JdbcClient 是 Spring 6.1 引入的一个 Jdbc 客户端工具类,提供了 Fluent 链式调用风格的查询和更新方法,支持 JDBC 风格的位置参数和 Spring 风格的命名参数绑定。 本文将带你了解,如何在使用 JdbcClient 执行 insert 操作时返回自增 ID。 关于 JdbcClient 更多详细的用法可以参阅 “Spring 6 JdbcClient API 指南” 和 “Spring Boot 中的新 JDBC 客户端: JdbcClient” 创建数据表 在本地 MYSQL 数据库 demo 中创建一张简单的 t_user 表,如下: CREATE TABLE `t_user` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `enabled` tinyint unsigned NOT NULL COMMENT '是否启用。0:禁用,1:启用', `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '名字', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户'; 其中, id 列是自增列。

给 Spring REST API 设置请求超时

1、概览 本文将带你了解给 Spring REST API 设置请求超时的几种方法。 当资源耗时过长时,请求超时机制可以避免糟糕的用户体验。当然也可以使用断路器模式(Circuit Breaker pattern)来实现,本文不细说。 2、@Transactional 超时 在数据库调用中实现请求超时的一种方法是利用 Spring 的 @Transactional 注解。它有一个 timeout 属性可以设置。该属性的默认值是 -1,相当于没有任何超时。 例如,假设将超时设置为 30 秒。如果注解方法的执行时间超过这个秒数,就会抛出异常。这对于回滚长时间运行的数据库查询可能很有用。 编写一个非常简单的 JPA Repository,它代表一个外部服务,该服务需要太长时间才能完成并导致超时。 这个 Repository 中有一个耗时的方法: public interface BookRepository extends JpaRepository<Book, String> { default int wasteTime() { Stopwatch watch = Stopwatch.createStarted(); // 延迟 2 秒 while (watch.elapsed(SECONDS) < 2) { int i = Integer.MIN_VALUE; while (i < Integer.MAX_VALUE) { i++; } } } } 如果在超时时间为 1 秒的事务中调用 wasteTime() 方法,超时时间将在方法执行完毕之前结束:

在 Spring AOP 中获取 Advise 方法信息

1、简介 本文将带你了解如何使用 Spring AOP Aspect 获取 Advise 方法的签名、参数、注解以及其他的额外信息。 2、Maven 依赖 首先,在 pom.xml 中添加 spring-boot-starter-aop Starter 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 3、创建 Pointcut 注解 创建一个 AccountOperation 注解,作为切面中的切点: @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AccountOperation { String operation(); } 注意,切点(Pointcut)不一定非要使用注解定义。也可以使用 Spring AOP 提供的 Pointcut 定义语言定义其他 Pointcut 类型,如类中的某些方法、以某些前缀开头的方法等。 4、创建示例 Service 4.1、Account 类 创建一个 Account POJO,带有 accountNumber 和 balance 属性。 在 service 方法中使用它作为方法参数: public class Account { private String accountNumber; private double balance; // get/set toString方法省略 } 4.

使用 WebClient 获取 JSON 对象集合

1、概览 从 Spring 5 开始,可以使用 WebClient 以响应式、非阻塞的方式执行服务之间的 REST 通信。WebClient 是新的 WebFlux 框架的一部分,构建于 Project Reactor 之上。它使用 Fluent 风格的响应式 API,底层实现使用 HTTP 协议。 当发起 Web 请求时,数据通常会以 JSON 格式返回,本文将带你了解如何使用 WebClient 将响应的 JSON 数组转换为 Java Object 数组、POJO 数组和 POJO 集合。 2、依赖 在 pom.xml 中添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.projectreactor</groupId> <artifactId>reactor-spring</artifactId> <version>1.0.1.RELEASE</version> </dependency> 3、JSON、POJO 和 Service 从端点 http://localhost:8080/readers 开始,它以 JSON 数组的形式返回读者最喜欢的书籍列表: [{ "id": 1, "name": "reader1", "favouriteBook": { "author": "Milan Kundera", "title": "The Unbearable Lightness of Being" } }, { "id": 2, "name": "reader2" "favouriteBook": { "author": "Douglas Adams", "title": "The Hitchhiker's Guide to the Galaxy" } }] 还需要相应的 Reader 和 Book 类来处理数据:

Spring RestTemplate 异常:“Not enough variables available to expand”

1、概览 本文将带你了解 Spring RestTemplate 抛出 IllegalArgumentException: Not enough variables available to expand 异常的原因以及解决办法。 2、原因 简而言之,当试图在 GET 请求参数中发送 JSON 数据时,通常会导致这个异常。 RestTemplate 提供了 getForObject 方法,通过在指定的 URL 上发出 GET 请求来获取表示对象。 出现异常的主要原因是 RestTemplate 将大括号中封装的 JSON 数据视为 URI 变量的占位符。 由于没有为预期的 URI 变量提供任何值,getForObject 方法就会抛出异常。 例如,尝试发送 {"name": "HP EliteBook"} 作为查询参数: String url = "http://products.api.com/get?key=a123456789z&criterion={\"name\":\"HP EliteBook\"}"; Product product = restTemplate.getForObject(url, Product.class); 将导致 RestTemplate 抛出异常: java.lang.IllegalArgumentException: Not enough variable values available to expand 'name' 3、示例应用 创建一个只有一个 GET 端点的基本 REST API 示例,来复现 RestTemplate 抛出 IllegalArgumentException 异常的情况。

Java 使用 RSA 进行加密、解密、签名和验签

RSA(Rivest-Shamir-Adleman)算法是一种非对称加密算法,广泛用于数据加密和数字签名领域。它是由 Ron Rivest、Adi Shamir 和 Leonard Adleman 于 1977 年共同提出的。 RSA 算法常用于如下场景: 公钥加密,私钥解密 私钥加密,公钥解密(不推荐) 私钥签名,公钥验签 生成密钥对 通过 Java java.security 包下的工具类可以生成 RSA 公钥和私钥。 package cn.springdoc.demo.test; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.Base64; /** * @author springdoc.cn * 生成 RSA 密钥对 */ public class Main { public static void main(String[] args) throws Exception { // 初始化 Key 生成器,指定算法类型为 RSA KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); // 密钥长度为 2048 位 keyPairGenerator.initialize(2048); // 生成密钥对 KeyPair keyPair = keyPairGenerator.

在 Spring 应用中防止跨站脚本(XSS)攻击

1、概览 在构建 Spring Web 应用时,关注安全性非常重要。跨站脚本 (XSS) 是对 Web 安全威胁最大的攻击之一。 在 Spring 应用中,防止 XSS 攻击是一项挑战。Spring 提供了内置的帮助来实现全面的保护。 本文将带你了解如何通过 Spring Security 使用 X-XSS-Protection 和 Content-Security-Policy 机制来防止 XSS 攻击。 2、什么是跨站脚本(XSS)攻击? 2.1、问题的定义 XSS 是一种常见的注入式攻击。在 XSS 中,攻击者试图在 Web 应用程序中执行恶意代码。他们通过 Web 浏览器或 HTTP 客户端工具(如 Postman)与应用交互。 XSS 攻击有两种类型: 反射性或非持久性 XSS 存储型或持久型 XSS 在反射型或非持久型 XSS 中,不受信任的用户数据被提交到 Web 应用,并立即在响应中返回,从而在页面中添加了不可信的内容。Web 浏览器会认为代码来自 Web 服务器并执行它。这可能会让黑客向你发送一个链接,当你点击该链接时,浏览器会从你使用的网站上获取你的私人数据,然后让你的浏览器将这些数据转发到黑客的服务器上。 在存储型或持久型 XSS 中,Web 服务器会存储攻击者的输入。随后,任何后来的访问者都可能执行该恶意代码。 2.2、防御攻击 防止 XSS 攻击的主要策略是清理用户输入。 在 Spring Web 应用中,用户的输入是 HTTP 请求。为防止攻击,应检查 HTTP 请求的内容,并删除可能可在服务器或浏览器中执行的任何内容。 对于通过 Web 浏览器访问的普通 Web 应用,可以使用 Spring Security 的内置功能(反射性 XSS)。

OpenFeign 上传文件

1、概览 Feign 是微服务中通过 REST API 以声明方式与其他微服务通信的强大工具,本文将带你了解如何使用 Open Feign 上传文件。 2、先决条件 假设如下 RESTful Web 服务用于文件上传: POST http://localhost:8081/upload-file 该 Web 服务端点定义如下: @PostMapping(value = "/upload-file") public String handleFileUpload(@RequestPart(value = "file") MultipartFile file) { // 文件上传逻辑 } 3、依赖 为了支持文件上传的 application/x-www-form-urlencoded 和 multipart/form-data 编码类型,需要添加 feign-core、 feign-form 和 feign-form-spring 模块。 在 Maven 中添加以下依赖: <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> <version>10.12</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.8.0</version> </dependency> 还可以直接使用 spring-cloud-starter-openfeign 依赖,它已经包含了 feign-core: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>3.1.0</version> </dependency> 4、配置 在 main 类中添加 @EnableFeignClients:

Spring 中的 @Component 注解

1、概览 本文将带你全面了解 Spring @Component 注解及相关领域。 2、Spring ApplicationContext 在了解 @Component 之前,首先需要了解一下 Spring ApplicationContext。 Spring ApplicationContext 是 Spring 保存对象实例的地方,Spring 已确定这些实例将被自动管理和分发。这些实例被称为 Bean。 Spring 的一些主要功能包括 Bean 管理和依赖注入。 利用控制反转(Inversion of Control),Spring 可以从应用中收集 Bean 实例,并在适当的时候使用它们。可以在 Spring 中定义 Bean 依赖,而无需处理这些对象的设置和实例化。 使用 @Autowired 等注解将 Spring 管理的 Bean 注入应用的能力是在 Spring 中创建功能强大且可扩展代码的驱动力。 那么,如何让 Spring 来管理的 Bean 呢?可以利用 Spring 的自动 Bean 检测功能,通过在类中使用元注解(Stereotype Annotation)来实现。 3、@Component @Component 是一个注解,它允许 Spring 自动检测自定义 Bean。 换句话说,无需编写任何明确的代码,Spring 就能做到: 扫描应用,查找注解为 @Component 的类 将它们实例化,并注入任何指定的依赖 在需要的地方注入 不过,大多数时候应该使用更专业的元(Stereotype)注解来实现这一功能。 3.1、Spring 元注解 Spring 提供了一些专门的元注解:@Controller、@Service 和 @Repository。它们都提供了与 @Component 相同的功能。

HttpMessageNotWritableException: "No converter found for return value of type"

1、概览 本文将带你了解 Spring 中出现 HttpMessageNotWritableException: "No converter found for return value of type" 异常的原因以及解决办法。 2、原因 通常,当 Spring 无法获取返回对象的属性时,就会出现这种异常。 导致这种异常的最典型原因通常是返回对象的属性没有任何 public getter 方法。 默认情况下,Spring Boot 依赖于 Jackson 库来完成序列化/反序列化请求和响应对象的所有工作。 因此,导致异常的另一个常见原因可能是 缺少或使用了错误的 Jackson 依赖。 简而言之,这种异常情况的一般准则是检查是否存在以下情况: 默认构造器 Getter 方法 Jackson 依赖 注意,异常类型 已从 java.lang.IllegalArgumentException 变为 org.springframework.http.converter.HttpMessageNotWritableException。 3、实例 现在,来看看一个会产生 org.springframework.http.converter.HttpMessageNotWritableException: "No converter found for return value of type" 异常的示例 使用 Spring Boot 构建一个基本的 REST API。 首先,创建 Student Model 类,并假装忘记生成 Getter 方法: public class Student { private int id; private String firstName; private String lastName; private String grade; public Student() { } public Student(int id, String firstName, String lastName, String grade) { this.