🎉🎉《Spring Boot实战案例合集》目前已更新131个案例,我们将持续不断的更新。文末有电子书目录。
💪💪永久更新承诺
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。
💌💌如何获取
订阅我们的合集《点我订阅》,并通过私信联系我们,我们将第一时间将电子书发送给您。
环境:SpringBoot3.4.2
1. 简介
将数据从一个系统发送到另一个系统是常见需求,尤其是在使用微服务或与外部服务集成时。实现这一功能的方法之一是触发出站 Webhook。Webhook 是一种简单的HTTP 请求,当应用程序内部发生特定事件时,它会向另一个服务发送请求。
Webhook 的工作原理及在 Spring Boot 中的实现
Webhook 允许你的应用通过推送数据来做出响应,而不是等待其他系统来请求数据。在 Spring Boot 中,这与框架支持的事件驱动风格非常契合。
Webhook 本质上就是普通的 HTTP 请求,不需要额外的库或第三方工具。它们在特定事件发生时触发,关键在于如何构造请求数据(payload)、发送到哪个 URL,以及如何以符合现代 Spring 实践的方式发起这个请求。
Webhook 的本质
Webhook 本质上是一种出站 HTTP 请求,通常是 POST 请求,由系统内部发生的某个事件触发。请求的内容携带了一些事件数据,帮助接收系统根据这些数据采取行动。
两个系统之间也不需要契约,只需要约定好数据格式。在大多数情况下,这种格式是 JSON。一个基本示例是系统在每次创建记录时发送一个 Webhook:
{"event": "order_created","data": {"id": 102,"createdAt": "2025-06-08 18:20:46"}}
接下来,我们将详细的介绍在Spring Boot中Webhook的实现。
2.1 如何发送HTTP请求
在 Spring 应用中,过去常用 RestTemplate 发送 HTTP 请求,它是同步的,适用于小型任务或阻塞环境。但在新项目中已不再推荐使用。虽然未被弃用,但 Spring 官方建议优先使用 WebClient。如下官方原话:
The RestClient offers a more modern API for synchronous HTTP access. For asynchronous and streaming scenarios, consider the reactive WebClient.
WebClient 是 spring-boot-starter-webflux 的一部分,无需完全使用响应式编程也能使用。它基于非阻塞模型,适用于传统 MVC 和响应式应用,API 简洁,支持所有 HTTP 方法,并能很好地处理 JSON 数据。
在使用前,可以创建一个配置类来定义共享的 WebClient Bean,方便在各处注入使用,避免重复配置。
public class WebhookClientConfig {public WebClient webClient(WebClient.Builder builder) {return builder.baseUrl("http://www.pack.com").defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build();}}
这个 bean 设置了一个默认的 base URL 和内容类型,使代码在使用时更加简洁。如果你需要针对多个外部系统,可以构建多个客户端(WebClient),或者在调用时动态设置 URL。
2.2 简单示例
接下来,我们通过一个简单的示例说明如何使用这个 WebClient 发送一个简单的 Webhook 请求:
public class WebhookSender {private final WebClient webClient;public WebhookSender(WebClient webClient) {this.webClient = webClient;}public void sendRecordCreatedEvent(Long recordId) {Map<String, Object> payload = Map.of("event", "order_created","data", Map.of("id", recordId, "timestamp", Instant.now().toString()));webClient.post().uri("/webhook-endpoint").bodyValue(payload).retrieve().toBodilessEntity().subscribe(); // 触发调用}}
WebClient 是非阻塞的,基于响应式流构建,只有调用 subscribe() 时才会执行。对于“即发即忘”的场景(如发送 Webhook),无需深入理解 Mono 或 Flux。它默认使用 Reactor Netty,性能更好,支持连接池和响应式线程模型,适合高并发场景。
相比传统同步客户端,它减少了线程阻塞,提升应用响应性,特别适用于微服务或突发流量场景。同时,WebClient 易于测试,可通过 @RestClientTest 或 MockWebServer 进行单元和集成测试。
2.3 事件出发Webhook逻辑
将 Webhook 与应用内的操作关联时,不必把 HTTP 代码直接写在业务逻辑中。更清晰的方式是分离触发和发送逻辑。Spring 的事件机制能很好地实现这一点:你可以在一处发布事件,在另一处监听并处理,两者无需直接耦合。这提高了模块化程度,并且易于扩展,比如一个事件可触发多个不同操作。
创建自定义事件
Spring 的事件系统无需继承 ApplicationEvent,任何对象(如record 或 POJO)都可以作为事件。框架通过事件类型自动匹配监听器,无需额外字段或复杂结构。如下事件:
public record OrderCompletedEvent(String orderNo) {}
发布事件
要触发事件,Spring 提供了 ApplicationEventPublisher 接口。大多数服务可以直接注入它并调用 publishEvent() 方法,传入你的自定义事件。它不需要任何配置,也不需要手动注册监听器。如下发布事件:
public class OrderService {private OrderService orderService ;private final ApplicationEventPublisher publisher;public OrderService(ApplicationEventPublisher publisher) {this.publisher = publisher;}public void completeOrder(Order data) {String orderNo = this.orderService.createOrder(data) ;publisher.publishEvent(new OrderCompletedEvent(orderNo));}public String createOrder(Order data) {// ...// 返回订单号return data.getOrderNo() ;}}
监听事件并发送Webhook
事件发布后,你需要一些东西来响应它并发送 HTTP 请求。Spring 使用 @EventListener 注解使这变得简单。你不需要扩展任何接口或手动注册监听器。只需用注解标记一个方法,Spring 会在事件类型匹配时调用它。
以下是一个事件监听处理程序,监听 OrderCompletedEvent 并使用 WebClient 发送 Webhook 的示例:
public class WebhookNotifier {private final WebClient webClient;private final OrderRepository orderRepository;public WebhookNotifier(WebClient webClient, OrderRepository orderRepository) {this.webClient = webClient;this.orderRepository = orderRepository;}(OrderCompletedEvent.class)public void onPurchaseCompleted(OrderCompletedEvent event) {String orderNo = event.orderNo() ;Order order = orderRepository.findByOrderNo(orderNo).orElse(null) ;if (order == null) {return;}Map<String, Object> payload = Map.of("event", "order_completed","data", Map.of("id", order.getId(),"price", order.getAmount(),"timestamp", order.getCreatedAt().toString()));webClient.post().uri("http://www.pack.com/webhooks/order").bodyValue(payload).retrieve().toBodilessEntity().subscribe();}}
2.4 可靠性优化
通过网络发送 HTTP 请求可能会失败,比如超时、被拒绝或返回 5xx 错误。虽然不必一开始就处理所有异常,但最好应对常见问题,避免系统卡住或日志被错误刷屏。
WebClient 可以使用响应式操作符(如 retryWhen)实现失败重试。你可以针对网络异常或特定状态码进行重试设置。例如,可以配置最多重试三次,每次间隔几秒。
如下修改,当失败是重试3次:
webClient.post().uri("http://www.pack.com/webhooks/order").bodyValue(payload).retrieve().toBodilessEntity().retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(4))).subscribe();
该示例,添加了一个简单的重试机制,带有固定的延迟。如果三次都失败,请求将被丢弃。你可以进一步扩展它,比如记录失败响应或使用数据库或消息队列将它们排队以供稍后重试,但这为你提供了一个干净的起点,而不会引入额外的复杂性。你还可以在 .retrieve() 之后链接 .doOnError(),如果你想捕获错误并记录它们。
基于事件的模式允许你将 Webhook 逻辑与其他所有逻辑隔离,这也包括错误处理。你可以根据失败的严重程度选择重试或静默失败。所有这些都不会影响触发事件的主服务。这种分离使系统保持稳定,并且更容易在以后进行更改。
推荐文章
天呐!Java Stream 高效开发!16 个逆天案例,让你秒速起飞!
非常实用!玩转 Spring Boot 接口参数类型转换,支持任意场景
优雅!Spring Boot 一个注解轻松实现敏感词检查,支持多场景
Jackson!JSON处理的瑞士军刀ObjectMapper


