教程

Spring WebSocket 简介

1、概览 本文将带你学习如何使用 Spring 4 中引入的 WebSocket 功能来实现一个简单的聊天应用。 WebSockets 是 Web 浏览器和服务器之间的一种双向、全双工、持久连接。一旦建立了 WebSocket 连接,该连接就会一直打开,直到客户端或服务器关闭该连接。 2、Maven 依赖 在 pom.xml 中添加所需的依赖: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>5.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>5.2.2.RELEASE</version> </dependency> 此外,还需要添加 Jackson 依赖,用于序列化/反序列化 JSON 格式的消息。 <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> 3、启用 WebSocket 首先,在配置类上通过 @EnableWebSocketMessageBroker 注解来启用 WebSocket 功能。 配置类需要继承 AbstractWebSocketMessageBrokerConfigurer。 顾名思义,它能在 Message Broker 的支持下处理 WebSocket 消息: @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.

Spring RestTemplate 解析 JSON 数组

1、概览 在 Spring Boot 应用中,一般使用 RestTemplate 来执行同步 HTTP 请求。数据通常以 JSON 的形式返回,而 RestTemplate 可以进行自动转换。 本文将带你了解,如何在 RestTemplate 中把响应的 JSON 数组转换为 Object 数组、POJO 数组和 POJO 集合。 2. JSON、POJO 和 Service 假设我们有一个端点 http://localhost:8080/users,它返回如下 JSON 格式的用户列表: [{ "id": 1, "name": "user1", }, { "id": 2, "name": "user2" }] 对应的 User 类如下: public class User { private int id; private String name; // get/set 方法省略 } 定义 Service 接口实现 UserConsumerServiceImpl,并注入 RestTemplate: public class UserConsumerServiceImpl implements UserConsumerService { private final RestTemplate restTemplate; public UserConsumerServiceImpl(RestTemplate restTemplate) { this.

Spring Bean 循环依赖

1、背景 有好几次线上发布老应用时,遭遇代码启动报错,具体错误如下: Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'xxxManageFacadeImpl': Bean with name 'xxxManageFacadeImpl' has been injected into other beans [xxxProductMaintenceFacadeImpl] in its raw version as part of a circular reference, but has eventually been wrap means thff, for expped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesallowEageOfType' with the 'allowEagerInit' flag turned off, for example 咋眼一看,这不就是 Spring Bean 循环依赖报错吗?脑海立马闪过那些年为了进阿里面试时被死亡 N 连问的场景,那时我们都知道 Spring 已经支持 Bean 循环依赖,为啥我们的 Spring Boot 应用启动时还报这个错误?于是,带着这个问题重新温习下 Spring 如何解决 Bean 循环依赖。

Spring Boot 自定义 Jackson ObjectMapper

1、概览 Spring Boot 默认使用 Jackson ObjectMapper 实例来序列化和反序列化 JSON 格式的响应与请求。 本文将带你了解如何在 Spring Boot 中自定义 Jackson ObjectMapper 选项,以及配置序列化和反序列化选项的最常用方法。 2、默认配置 默认情况下,Spring Boot 禁用了以下功能: MapperFeature.DEFAULT_VIEW_INCLUSION DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 从一个简单的例子开始: 客户端向 /coffee?name=Lavazza 端点发送 GET 请求。 Controller 返回一个新的 Coffee 对象。 Spring 使用 ObjectMapper 将 POJO 序列化为 JSON。 使用 String 和 LocalDateTime 对象来演示自定义选项: public class Coffee { private String name; private String brand; private LocalDateTime date; // get / set 省略 } 定义一个简单的 REST Controller 来演示序列化: @GetMapping("/coffee") public Coffee getCoffee( @RequestParam(required = false) String brand, @RequestParam(required = false) String name) { return new Coffee() .

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.