Spring WebFlux 项目实战 在Spring WebFlux中创建多个RouterFunctions

编程教程 > Java > Spring (3878) 2024-11-26 14:39:04

引言

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

如果你是WebFlux的新手,我推荐看看我以前的博客,[Spring WebFlux 项目实战],在那里我写了一些关于这个主题的完整例子和解释。
 

所以我们先假设置场景。你的应用程序中有两个不同的域,例如人员和位置。你可能想要让它们不仅在逻辑上而且在你的代码中彼此分离。要做到这一点,您需要一种方法来定义与其他域相互隔离的路由。这是我们将在这篇文章中看到的内容。

如果你认为你已经知道这个问题的答案,那么你可能是对的。这真的很简单。尽管如此,让我们继续努力吧。要为人员域创建路由,请创建一个RouterFunction映射到相关处理函数的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
所以,现在我们要添加位置逻辑的路由。我们可以简单地将路由添加到此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中的依赖关系的数量所淹没。

解决这个问题的方法是创建多个RouterFunctionbean。而已。因此,如果我们在人员域中创建一个,PersonRouter并且在位置域中指定一个LocationRouter,则每个人都可以定义他们需要的路由,而Spring将完成剩下的任务。这是有效的,因为Spring遍历应用程序上下文并找到或创建任何RouterFunctionbean并将它们合并为一个函数供以后使用。

使用这些信息,我们可以编写下面的代码。
 

@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豆创建中WebFluxConfigurationSupport这是春天WebFlux配置震中。通过@EnableWebFlux在配置类中包含注释或者依靠自动配置,一系列事件开始并收集我们所有的RouterFunctions就是其中之一。

下面是这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;
    }
  }

}

检索所有路由的路径开始于创建bean afterPropertiesSet后调用RouterFunctionMapping。由于它是内部的RouterFunctionnull它会initRouterFunctions触发一系列导致执行的方法routerFunctions。一个新SortedRouterFunctionsContainer的构造(私有静态类)routerFunctions通过注入RouterFunction应用程序上下文中的所有s来设置它的字段。这工作,因为Spring会注入类型的所有豆类T一个当List<T>被注入。现在检索到的RouterFunctions被组合在一起以制作一个RouterFunction从现在开始用于将所有传入请求路由到适当处理程序的单个节点。

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

我建议看看我以前的帖子[Spring WebFlux 项目实战]


评论
User Image
提示:请评论与当前内容相关的回复,广告、推广或无关内容将被删除。

相关文章
Spring WebFlux 项目实战 在Spring WebFlux中创建多个RouterFunctions,在这篇文章中,我们将着眼于在Spring WebFlux中将多个路由器功能定义到不...
spring boot webflux client实战,webclient是spring webflux的一个小组件。对于Java的http通讯来说,webclient是非常简单易用的。
Spring WebFlux,spring框架5.0将会新增的web增强框架,这里主要讲述什么是Spring WebFlux以及Spring WebFlux的新功能,Spring WebFlux...
Spring WebFlux入门程序hello word。本文主要在于讲解如何创建和运行spring webflux入门程序hello word。其实不难发现和spring mvc相比代码层基本...
1.引言Spring 5通过引入一种名为Spring WebFlux的全新反应框架来支持响应式编程范例
引言Spring Boot 2.0最近去了GA,所以我决定写我关于Spring的第一篇文章很长一段时间
Spring 5 入门实战Say Hello,本博客主要讲解spring 5最基础的容器入门实战。
1.引言Spring开发人员,您是否曾经觉得需要一个易于使用且高效的流畅功能样式 API 的异步/非阻塞 HTTP客户端?如果是,那么我欢迎您阅读关于WebClient的文章,WebClient...
Spring Boot 2.0 有哪些新特性_Spring Boot 2.0新功能,在本文中,我们将探讨为Spring Boot 2.0计划的一些更改和功能。我们还会描述这些变化如何帮助我们提高...
从Spring 6和Spring Boot 3开始,Spring framework支持将远程HTTP服务代理为带有HTTP交换注解方法的Java接口。类似的库,如OpenFeign和Retro...
Spring Boot 2.0 入门 logoback配置实战教程,俗话说好马配好鞍。Spring Boot 框架从各方面给我们带来了开发效率。日志自然也不会落下。本文将讲解与Spring Bo...
从Spring 6和Spring Boot 3开始,Spring框架支持“HTTP API的问题详细信息”规范RFC 7807。本Spring Boot 教程将详细指导您完成这一新增强。1.问题...
Spring MVC 5 接受对象集合参数实战,在之前,我一直以为http传输的参数是KEY-VALUE键值对的方式和文件流的形式。直到最近遇到新需求才知道还有一种RAW的数据类型。通过这种原生...
引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写...
引言在本文中,我们将讨论有关Spring启动安全性和JWT令牌的OAUTH2实现以及保护REST API