教程

Spring Cloud Feign 集成测试

1、概览 本文将带你了解 Feign 客户端的集成测试。 首先创建一个基本的 Open Feign 客户端,并使用 WireMock 编写一个简单的集成测试。 之后,给客户端添加 Ribbon 配置,并为其构建一个集成测试。最后,配置一个 Eureka 测试容器,并测试此设置,以确保整个配置按预期工作。 2、Feign Client 要设置 Feign 客户端,首先要添加 Spring Cloud OpenFeign Maven 依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 然后,创建一个 Book 模型类: public class Book { private String title; private String author; } 最后,创建 Feign 客户端接口: @FeignClient(name = "books-service") public interface BooksClient { @RequestMapping("/books") List<Book> getBooks(); } 现在,我们有了一个从 REST 服务中获取 List<Book> 的 Feign 客户端。接下来,编写一些集成测试。 3、WireMock 3.1、设置 WireMock 服务器 要测试 BooksClient,需要一个提供 /books 端点的 mock 服务,客户端将调用该 mock 服务。为此,我们使用 WireMock。

Spring-boot:repackage 和 Maven package

1、概览 Apache Maven 是一种广泛使用的项目依赖管理工具和项目构建工具。 Spring Boot 通过 Spring Boot Maven Plugin 在 Apache Maven 中提供了对 Spring Boot 的支持。 众所周知,在 Maven 中可以使用 mvn package 将应用打包为 JAR 或 WAR 包。不过,Spring Boot Maven 插件额外添加了一个 repackage goal,也可以在 mvn 命令中调用。 有时,这两个 mvn 命令会让人混淆。本文将带你了解 mvn package 和 spring-boot:repackage 之间的区别。 2、Spring Boot 应用示例 首先,创建一个简单的 Spring Boot 应用作为示例: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } 创建一个简单的 REST 端点来验证应用是否正常运行: @RestController public class DemoRestController { @GetMapping(value = "/welcome") public ResponseEntity welcomeEndpoint() { return ResponseEntity.

将 Hibernate 代理对象转换为实际的实体对象

1、概览 本文将带你了解 Hibernate 是在什么时候创建代理对象的,代理对象有啥用?以及如何把 Hibernate 的代理对象转换为真正的实体对象。 2、Hibernate 何时创建代理对象? Hibernate 使用代理对象来实现懒加载。 有如下 PaymentReceipt 和 Payment 实体: @Entity public class PaymentReceipt { ... @OneToOne(fetch = FetchType.LAZY) private Payment payment; ... } @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public abstract class Payment { ... @ManyToOne(fetch = FetchType.LAZY) protected WebUser webUser; ... } 加载这两个实体中的任何一个,都会导致 Hibernate 为关联字段使用 FetchType.LAZY 创建一个代理对象。 创建并运行集成测试: @Test public void givenPaymentReceipt_whenAccessingPayment_thenVerifyType() { PaymentReceipt paymentReceipt = entityManager.find(PaymentReceipt.class, 3L); Assert.assertTrue(paymentReceipt.getPayment() instanceof HibernateProxy); } 测试代码如上,加载了一个 PaymentReceipt,并验证了 payment 对象不是 CreditCardPayment 的实例,而是一个 HibernateProxy 对象。

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)。