源码精读:AbstractWebEndpointHandlerMapping —— 理解 Servlet 与 WebFlux 的公共逻辑提取

🔍 源码精读:AbstractWebEndpointHandlerMapping —— 理解 Servlet 与 WebFlux 的公共逻辑提取

在 Spring Boot Actuator 中,AbstractWebEndpointHandlerMapping 是一个抽象基类,用于提取 Servlet(MVC)Reactor(WebFlux) 两种模型下 Web 端点处理的公共逻辑

本文将带你深入 AbstractWebEndpointHandlerMapping 源码,解析它是如何实现“一次定义,多模型适配”的设计哲学,理解 Spring Boot 的抽象能力。


一、类图关系与作用

AbstractWebEndpointHandlerMapping (抽象基类)
       │
       ├───────────────┐
       ↓               ↓
ControllerEndpointHandlerMapping     WebFluxEndpointHandlerMapping
  (Servlet 模型)                      (WebFlux 模型)

📁 源码路径:

org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebEndpointHandlerMapping

⚠️ 注意:虽然路径是 servlet,但它被 Servlet 和 WebFlux 模型共同继承


二、核心职责

AbstractWebEndpointHandlerMapping 封装了两种模型共有的逻辑:

职责说明
✅ 端点路径映射构建 /actuator/{id} 路径
✅ CORS 配置支持跨域
✅ 媒体类型处理支持 application/json
✅ 安全配置集成 Spring Security
✅ 基础属性注入WebEndpointPropertiesEndpointMediaTypes

✅ 它不关心“如何处理请求”,只关心“如何配置和初始化”。


三、源码结构解析

public abstract class AbstractWebEndpointHandlerMapping extends AbstractEndpointHandlerMapping {

    protected AbstractWebEndpointHandlerMapping(
            Collection<ExposableWebEndpoint> endpoints,
            EndpointMediaTypes mediaTypes,
            String basePath,
            CorsConfiguration corsConfiguration) {

        super(endpoints, mediaTypes, basePath);
        setCorsConfiguration(corsConfiguration);
    }

    // 提供 protected 方法供子类使用
    protected void initCorsConfiguration(CorsConfiguration corsConfiguration) {
        // 初始化 CORS
    }

    @Override
    protected void customizeLookupPath(EndpointId endpointId, RequestMappingInfo.Builder builder) {
        // 可选:自定义路径查找逻辑
    }
}

它继承自更上层的 AbstractEndpointHandlerMapping


四、AbstractEndpointHandlerMapping —— 更高层抽象

📁 路径:

org.springframework.boot.actuate.endpoint.web.AbstractEndpointHandlerMapping

核心功能:

public abstract class AbstractEndpointHandlerMapping
        implements ApplicationContextAware, InitializingBean {

    private final Collection<ExposableWebEndpoint> endpoints;
    private final EndpointMediaTypes mediaTypes;
    private final String basePath;

    public AbstractEndpointHandlerMapping(
            Collection<ExposableWebEndpoint> endpoints,
            EndpointMediaTypes mediaTypes,
            String basePath) {
        Assert.notNull(endpoints, "Endpoints must not be null");
        Assert.notNull(mediaTypes, "MediaTypes must not be null");
        Assert.hasText(basePath, "BasePath must not be empty");
        this.endpoints = endpoints;
        this.mediaTypes = mediaTypes;
        this.basePath = basePath.startsWith("/") ? basePath : "/" + basePath;
    }

    @Override
    public void afterPropertiesSet() {
        // 子类实现初始化
        initHandlerMethods();
    }

    protected abstract void initHandlerMethods();
}

✅ 这是所有 Web 端点映射的“根”抽象类。


五、公共逻辑提取详解

1. 构造函数:统一初始化

super(endpoints, mediaTypes, basePath);
  • endpoints:来自 WebEndpointDiscoverer.getEndpoints()
  • mediaTypes:请求/响应的 MIME 类型(默认 application/json
  • basePath:来自 WebEndpointProperties.basePath(默认 /actuator

✅ 所有子类都使用相同的初始化参数。


2. 路径构建:getBasePath() + endpointId

String path = getBasePath() + "/" + endpoint.getEndpointId();
  • getBasePath() 来自父类
  • endpoint.getEndpointId()@Endpoint(id = "xxx")
  • 生成 /actuator/health/actuator/metrics 等路径

✅ 两种模型都使用相同的路径规则。


3. CORS 配置统一处理

setCorsConfiguration(corsConfiguration);
  • 支持 management.endpoints.web.cors.* 配置
  • 如:
    management:
      endpoints:
        web:
          cors:
            allowed-origins: "https://admin.example.com"
            allowed-methods: "GET,POST"
    

✅ 子类无需重复实现 CORS 逻辑。


4. 媒体类型支持

protected final EndpointMediaTypes mediaTypes;
  • 用于设置 @RequestMappingproduces
  • 确保返回 application/json

✅ 统一内容协商机制。


六、子类如何差异化实现?

虽然公共逻辑被提取,但两种模型的请求处理机制完全不同,由子类各自实现。

1. ControllerEndpointHandlerMapping(Servlet)

@Override
protected void initHandlerMethods() {
    for (ExposableWebEndpoint endpoint : getEndpoints()) {
        registerHandlerMethod(endpoint.getEndpointInstance(), mappingFor(endpoint));
    }
}
  • 使用 registerHandlerMethod() 注册到 Spring MVC 的 handlerMethods 映射表
  • 基于 HandlerMethod 反射调用

2. WebFluxEndpointHandlerMapping(WebFlux)

@Override
public Mono<HandlerResult> getHandler(ServerWebExchange exchange) {
    return getHandlerInternal(exchange).flatMap(this::createHandlerResult);
}

private Mono<ExposableWebEndpoint> getHandlerInternal(ServerWebExchange exchange) {
    String lookupPath = exchange.getRequest().getURI().getRawPath();
    return findEndpoint(this.endpoints, lookupPath)
        .switchIfEmpty(Mono.empty())
        .map(Mono::just)
        .orElse(Mono.empty());
}
  • 实现 WebHandler 接口
  • 返回 Mono<HandlerResult>,完全响应式

✅ 差异被封装在 initHandlerMethods()getHandler() 中。


七、设计模式:模板方法(Template Method)

AbstractWebEndpointHandlerMapping 典型使用了 模板方法模式

public abstract class AbstractEndpointHandlerMapping
        implements InitializingBean {

    @Override
    public final void afterPropertiesSet() {
        initHandlerMethods(); // 子类实现
    }

    protected abstract void initHandlerMethods();
}
  • afterPropertiesSet() 是模板方法(固定流程)
  • initHandlerMethods() 是抽象方法(由子类实现)

✅ 实现“流程统一,细节可变”。


八、完整调用流程对比

共同流程(由父类完成)

1. 构造 AbstractWebEndpointHandlerMapping
   ├─ 注入 endpoints, mediaTypes, basePath, cors
   ↓
2. afterPropertiesSet() → 调用 initHandlerMethods()
   ↓
3. 构建路径:/actuator/{id}
   ↓
4. 配置 CORS、Media Types

分支流程(由子类实现)

模型子类处理方式
ServletControllerEndpointHandlerMapping注册 HandlerMethod 到 Spring MVC
WebFluxWebFluxEndpointHandlerMapping实现 WebHandler.getHandler() 返回 Mono

九、源码价值总结

优势说明
代码复用避免 Servlet 和 WebFlux 重复实现路径、CORS、媒体类型等逻辑
扩展性新增 Web 模型(如 gRPC)可继承此抽象类
维护性修改 basePath 或 CORS 逻辑只需改一处
一致性两种模型的端点路径、行为保持一致
解耦将“配置”与“执行”分离

十、如何验证抽象有效性?

方法一:修改 basePath

management:
  endpoints:
    web:
      base-path: /manage

启动后:

  • /manage/health 可访问(Servlet)
  • /manage/health 可访问(WebFlux)

✅ 证明路径逻辑被统一管理。


方法二:启用 CORS

management:
  endpoints:
    web:
      cors:
        allowed-origins: "https://admin.example.com"

两种模型下都能正确返回 Access-Control-Allow-Origin


✅ 总结:AbstractWebEndpointHandlerMapping 的设计精髓

层级职责
L1AbstractEndpointHandlerMapping最高层抽象:端点集合、basePath、mediaTypes
L2AbstractWebEndpointHandlerMappingWeb 层抽象:CORS、安全、路径定制
L3ControllerEndpointHandlerMappingServlet 实现:HandlerMethod
L3WebFluxEndpointHandlerMappingWebFlux 实现:WebHandler + Mono

🎯 核心思想
公共逻辑上提,差异实现下放,实现“一次定义,多模型适配”。


📘 收获

  • 理解了 Spring Boot Actuator 如何通过继承 + 模板方法支持多编程模型。
  • 掌握了 AbstractWebEndpointHandlerMapping 是如何提取公共逻辑的。
  • 为后续阅读其他 AbstractXXX 类(如 AbstractEndpoint)提供了范式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值