在Spring WebFlux中创建多个RouterFunction

本文探讨在Spring WebFlux中为不同逻辑域定义多个路由器功能。通过创建单独的bean,每个域可以独立定义路由,避免代码耦合。文章详细解释了如何通过Spring自动收集和组合这些RouterFunction,实现请求的正确路由。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这篇文章中,我们将研究在Spring WebFlux中为不同的逻辑域定义多个路由器功能。 如果您正在创建“微服务”,则可能不会出现问题,因为您很可能仅在每个服务的单个域中工作,但是如果不是这样,则可能需要在应用程序中包含多个域,用户或您自己的服务可以进行交互。 做到这一点的代码就像我希望的那样简单,可以用几句话来解释。 为了使本文更加有趣,我们将看一些使这一切成为可能的Spring代码。

如果您不熟悉WebFlux,建议您阅读我以前的文章[使用Spring WebFlux做事]( https://lankydanblog.com/2018/03/15/doing-stuff-with-spring-webflux/ ),在这里,我写了一些有关该主题的详尽示例和解释。

因此,让我们先设置场景。 您的应用程序内有两个不同的域,例如人和地点。 您可能希望不仅在逻辑上而且在代码内使它们彼此分开。 为此,您需要一种独立于彼此域定义路由的方法。 这就是我们将在这篇文章中看到的内容。

如果您认为您已经知道该问题的答案,那么您可能是对的。 真的就是这么简单。 让我们继续努力吧。 要仅为人员域创建路由,请创建一个RouterFunction bean,该bean映射到相关的处理程序函数,如下所示。

@Configuration
public class MyRouter {
  // works for a single bean
  @Bean
  public RouterFunction<ServerResponse> routes(PersonHandler personHandler) {
    return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get)
        .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all)
        .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post)
        .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put)
        .andRoute(DELETE("/people/{id}"), personHandler::delete)
        .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry);
  }
}

这将创建到PersonHandler各种处理程序函数的PersonHandler

因此,现在我们要为位置逻辑添加路由。 我们可以简单地将路由添加到该bean,如下所示。

@Configuration
public class MyRouter {
  // not ideal!
  @Bean
  public RouterFunction<ServerResponse> routes(PersonHandler personHandler, LocationHandler locationHandler) {
    return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get)
        .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all)
        .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post)
        .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put)
        .andRoute(DELETE("/people/{id}"), personHandler::delete)
        .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry)
        .andRoute(GET("/locations/{id}").and(accept(APPLICATION_JSON)), locationHandler::get);
  }
}

Bean现在包含对LocationHandler的引用,因此可以设置位置路由。 该解决方案的问题在于它需要将代码耦合在一起。 此外,如果您需要添加更多的处理程序,很快就会被注入到此bean中的依赖项数量所淹没。

解决此问题的方法是创建多个RouterFunction bean。 而已。 因此,如果我们在人员域中创建一个,例如PersonRouter然后在位置域中创建一个名为LocationRouter域,则每个域都可以定义所需的路由,其余的将由Spring完成。 之所以可行,是因为Spring会遍历应用程序上下文并查找或创建任何RouterFunction bean,并将它们合并为一个函数供以后使用。

使用此信息,我们可以编写以下代码。

@Configuration
public class PersonRouter {
  // solution
  @Bean
  public RouterFunction<ServerResponse> peopleRoutes(PersonHandler personHandler) {
    return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get)
        .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all)
        .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post)
        .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put)
        .andRoute(DELETE("/people/{id}"), personHandler::delete)
        .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry);
  }
}

@Configuration
public class LocationRouter {
  // solution
  @Bean
  public RouterFunction<ServerResponse> locationRoutes(LocationHandler locationHandler) {
    return RouterFunctions.route(GET("/locations/{id}").and(accept(APPLICATION_JSON)), locationHandler::get);
  }
}

PersonRouter可以与其他人/人相关的代码保持在一起,而LocationRouter可以做到相同。

为了使它变得更有趣,为什么要这样做?

RouterFunctionMapping是检索在应用程序上下文内创建的所有RouterFunction Bean的类。 RouterFunctionMapping bean是在WebFluxConfigurationSupport中创建的, WebFluxConfigurationSupport是Spring WebFlux配置的中心。 通过在配置类中包含@EnableWebFlux批注或依靠自动配置,将启动一系列事件,收集我们所有的RouterFunction就是其中之一。

下面是RouterFunctionMapping类。 我删除了它的构造函数和一些方法,以使此处的代码片段更容易理解。

public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {

  @Nullable
  private RouterFunction<?> routerFunction;

  private List<HttpMessageReader<?>> messageReaders = Collections.emptyList();

  // constructors

  // getRouterFunction

  // setMessageReaders

  @Override
  public void afterPropertiesSet() throws Exception {
    if (CollectionUtils.isEmpty(this.messageReaders)) {
      ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create();
      this.messageReaders = codecConfigurer.getReaders();
    }

    if (this.routerFunction == null) {
      initRouterFunctions();
    }
  }

  /**
   * Initialized the router functions by detecting them in the application context.
   */
  protected void initRouterFunctions() {
    if (logger.isDebugEnabled()) {
      logger.debug("Looking for router functions in application context: " +
          getApplicationContext());
    }

    List<RouterFunction<?>> routerFunctions = routerFunctions();
    if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
      routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
    }
    this.routerFunction = routerFunctions.stream()
        .reduce(RouterFunction::andOther)
        .orElse(null);
  }

  private List<RouterFunction<?>> routerFunctions() {
    SortedRouterFunctionsContainer container = new SortedRouterFunctionsContainer();
    obtainApplicationContext().getAutowireCapableBeanFactory().autowireBean(container);

    return CollectionUtils.isEmpty(container.routerFunctions) ? Collections.emptyList() :
        container.routerFunctions;
  }

  // getHandlerInternal

  private static class SortedRouterFunctionsContainer {

    @Nullable
    private List<RouterFunction<?>> routerFunctions;

    @Autowired(required = false)
    public void setRouterFunctions(List<RouterFunction<?>> routerFunctions) {
      this.routerFunctions = routerFunctions;
    }
  }

}

检索所有路由的路径始于afterPropertiesSet ,该RouterFunctionMapping在创建RouterFunctionMapping bean之后调用。 由于内部的RouterFunctionnull因此它会调用initRouterFunctions触发一系列导致routerFunctions执行的routerFunctions 。 通过从应用程序上下文注入所有RouterFunction构造一个新的SortedRouterFunctionsContainer (私有静态类),将其设置为routerFunctions字段。 这是可行的,因为在注入List<T>时,Spring会注入所有T类型的bean。 现在检索到的RouterFunction组合在一起,形成一个RouterFunction ,从现在开始将其用于将所有传入请求路由到适当的处理程序。

这里的所有都是它的。 总之,为不同的业务域定义多个RouterFunction非常简单,因为您只需在它们最有意义的任何区域中创建它们,Spring就会开始获取它们。 为了使某些魔术变得神秘,我们研究了RouterFunctionMapping以了解如何收集和组合我们创建的RouterFunction ,以便可以将它们用于将请求路由到处理程序。 作为结束语,我确实了解到,这篇文章在某些方面是微不足道的,但有时看似明显的信息可能会很有帮助。

如果您还没有这样做,我建议您看一下我以前的文章《使用Spring WebFlux做事》( https://lankydanblog.com/2018/03/15/doing-stuff-with-spring-webflux/ )。

最后,如果您发现这篇文章很有帮助,并且希望在我撰写新文章时保持关注,那么可以在Twitter上通过@LankyDanDev关注我。

翻译自: https://www.javacodegeeks.com/2018/03/creating-multiple-routerfunctions-in-spring-webflux.html

<think>好的,我需要帮助用户比较Spring WebFluxSpring Web的区别以及它们的适用场景。首先,我应该回顾一下这两个框架的基本概念和它们的设计理念。 Spring Web是基于Servlet API构建的,属于传统的同步阻塞式框架,主要依赖于每个请求对应一个线程的模型。这在处理高并发时可能会遇到性能瓶颈,因为线程的创建和切换会有开销。而Spring WebFlux则是响应式编程框架,支持非阻塞的异步处理,能够更高效地利用系统资源,适合处理大量并发请求。用户可能想知道这两者在架构上的具体差异,比如是否都支持MVC模式,或者如何处理请求。 接下来,我需要考虑它们的编程模型差异。Spring Web通常使用命令式编程,代码按顺序执行,容易理解和调试。而WebFlux使用响应式编程,基于Reactor库,通过Flux和Mono来处理数据流,这可能需要开发者适应新的编程范式。用户可能关心学习曲线和现有代码的迁移难度。 性能方面,WebFlux在IO密集型场景下表现更好,尤其是在高并发情况下,因为它避免了线程阻塞,能够用较少的线程处理更多请求。但如果是计算密集型任务,可能优势不明显,甚至因为上下文切换而影响性能。需要明确适用场景,比如微服务架构中的网关或实时数据处理。 适用场景部分,应该分情况讨论。比如,如果项目已经使用Spring MVC且运行良好,可能不需要迁移到WebFlux。但如果需要处理大量并发或延迟敏感的应用,如实时聊天、流处理,WebFlux会更合适。同时,要考虑团队对响应式编程的熟悉程度,因为开发模式不同可能影响项目进度。 还需要引用站内的资料,比如引用[1]提到Spring Cloud的版本和配置,可能和微服务相关,可以联系到WebFlux在云原生环境中的应用。引用[4]关于NoSQL的扩展性,可能和响应式系统的分布式特性有关联,说明WebFlux适合需要水平扩展的场景。此外,引用[2]提到的构建工具支持,可能在项目配置或打包时需要考虑,虽然不直接相关,但可以间接说明生态系统的成熟度。 最后,生成相关问题时要覆盖性能对比、迁移策略、与其他技术的整合(如数据库访问),以及实际案例。确保问题能够帮助用户进一步探索这两个框架的细节。</think>### Spring WebFlux vs Spring Web: 核心区别与适用场景分析 #### 1. **架构差异** - **Spring Web (Servlet-based)** - 基于 **Servlet API** 和 **阻塞式I/O模型**,每个请求占用一个线程直至响应完成[^1]。 - 依赖传统线程池处理并发,适合低至中等并发场景。 - 支持同步编程模型,例如: ```java @GetMapping("/user") public User getUser() { return userService.findUser(); // 同步阻塞调用 } ``` - **Spring WebFlux (Reactive)** - 基于 **Reactive Streams** 规范,采用 **非阻塞异步I/O**(如Netty或Undertow服务器)[^4]。 - 使用事件循环(Event Loop)处理高并发请求,例如: ```java @GetMapping("/user") public Mono<User> getUser() { return userService.findUserReactive(); // 返回Mono非阻塞流 } ``` - 支持响应式编程,通过`Flux`和`Mono`处理数据流。 #### 2. **编程模型对比** | 特性 | Spring Web | Spring WebFlux | |--------------------|----------------------------|-----------------------------| | 编程范式 | 命令式(同步/阻塞) | 声明式(异步/非阻塞) | | 线程模型 | 每个请求占用独立线程 | 少量线程处理所有请求 | | 适用场景 | CPU密集型或低并发任务 | I/O密集型或高并发任务 | | 兼容性 | 依赖Servlet容器(如Tomcat) | 支持Netty、Undertow等服务器 | #### 3. **性能与扩展性** - **高并发场景**:WebFlux 在每秒数万级请求下表现更优(如实时推送、API网关)。 - **资源消耗**:WebFlux 通过复用线程减少内存占用,适合云原生和微服务环境[^1]。 - **限制**:若业务逻辑含复杂计算(如密集循环),WebFlux 的异步优势可能被抵消。 #### 4. **适用场景** - **优先选择 Spring Web 的情况**: - 已有基于Spring MVC的遗留系统。 - 业务逻辑以同步数据库访问为主(如JDBC)。 - 团队熟悉传统编程模型。 - **优先选择 WebFlux 的情况**: - 需要处理高并发或长连接(如WebSocket、实时监控)。 - 后端依赖响应式数据库(如MongoDB Reactive、R2DBC)。 - 云原生架构中要求弹性扩展[^1]。 #### 5. **迁移注意事项** - **渐进式迁移**:可通过`RouterFunction`与`@Controller`共存逐步替换。 - **响应式驱动**:需配套使用响应式数据库和中间件(如Kafka Reactive Streams)。 - **调试复杂度**:异步链式调用需借助工具(如Reactor Debug Agent)跟踪异常。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值