复制下载ZY : https://www.97yrbl.com/t-1385.html咱们知道,Spring-Cloud-Gateway 其实底层也是依据 Spring Boot 的。首要来看下 Spring Boot 中初始化哪种 web 容器的选择原理:首要第一步是依据类是否存在确定是哪种 WebApplicationType:
WebApplicationType
public enum WebApplicationType {
  /**
   * 没有 web 服务,不需要 web 容器
   */
  NONE,
  /**
   * 运用依据 servlet 的 web 容器
   */
  SERVLET,
  /**
   * 运用照应式的 web 容器
   */
  REACTIVE;
  
  private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
      "org.springframework.web.context.ConfigurableWebApplicationContext" };
  private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
  private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
  private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
  private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
  private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
  static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
        && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
      return WebApplicationType.REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
      if (!ClassUtils.isPresent(className, null)) {
        return WebApplicationType.NONE;
      }
    }
    return WebApplicationType.SERVLET;
  }
拷贝代码 拷贝代码
复制代码
复制代码从源码中能够看出,当有 WEBFLUX_INDICATOR_CLASS 而且没有 WEBMVC_INDICATOR_CLASS 以及 JERSEY_INDICATOR_CLASS 的时分,判别为 REACTIVE 环境。假定全部 SERVLET_INDICATOR_CLASSES 就以为是 SERVLET 环境。其实这样也能够看出,假定又引入 spring-web 又引入 spring-webflux 的依靠,其实仍是 SERVLET 环境。假定以上都没有,那么就是无 web 容器的环境。在 Spring-Cloud-Gateway 中,是 REACTIVE 环境。
假定是 REACTIVE 环境,就会运用org.springframework.boot.web.reactive.server.ReactiveWebServerFactory的完成 Bean 创立 web 容器。那么究竟是哪个完成呢?现在有四个完成(Spring-boot 2.7.x):
- TomcatReactiveWebServerFactory:依据 Tomcat 的照应式 web 容器 Factory
 - JettyReactiveWebServerFactory:依据 Jetty 的照应式 web 容器 Factory
 - UndertowReactiveWebServerFactory:依据 Undertow 的照应式 web 容器 Factory
 - NettyReactiveWebServerFactory:依据 Netty 的照应式 web 容器 Factory
 
实践会用哪个,看究竟哪个 Bean 会注册到 ApplicationContext 中:
ReactiveWebServerFactoryConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ HttpServer.class })
static class EmbeddedNetty {
  @Bean
  @ConditionalOnMissingBean
  ReactorResourceFactory reactorServerResourceFactory() {
    return new ReactorResourceFactory();
  }
  @Bean
  NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory,
      ObjectProviderroutes, ObjectProviderserverCustomizers) {
    NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
    serverFactory.setResourceFactory(resourceFactory);
    routes.orderedStream().forEach(serverFactory::addRouteProviders);
    serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
    return serverFactory;
  }
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ org.apache.catalina.startup.Tomcat.class })
static class EmbeddedTomcat {
  @Bean
  TomcatReactiveWebServerFactory tomcatReactiveWebServerFactory(
      ObjectProviderconnectorCustomizers,
      ObjectProvidercontextCustomizers,
      ObjectProvider> protocolHandlerCustomizers) {
    TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
    factory.getTomcatConnectorCustomizers()
        .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
    factory.getTomcatContextCustomizers()
        .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
    factory.getTomcatProtocolHandlerCustomizers()
        .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
    return factory;
  }
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ org.eclipse.jetty.server.Server.class, ServletHolder.class })
static class EmbeddedJetty {
  @Bean
  @ConditionalOnMissingBean
  JettyResourceFactory jettyServerResourceFactory() {
    return new JettyResourceFactory();
  }
  @Bean
  JettyReactiveWebServerFactory jettyReactiveWebServerFactory(JettyResourceFactory resourceFactory,
      ObjectProviderserverCustomizers) {
    JettyReactiveWebServerFactory serverFactory = new JettyReactiveWebServerFactory();
    serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
    serverFactory.setResourceFactory(resourceFactory);
    return serverFactory;
  }
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ Undertow.class })
static class EmbeddedUndertow {
  @Bean
  UndertowReactiveWebServerFactory undertowReactiveWebServerFactory(
      ObjectProviderbuilderCustomizers) {
    UndertowReactiveWebServerFactory factory = new UndertowReactiveWebServerFactory();
    factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
    return factory;
  }
}
拷贝代码 拷贝代码
复制代码
复制代码从原码能够看出,每种配备上都有@ConditionalOnMissingBean(ReactiveWebServerFactory.class)以及判别是否有对应容器的 class 的条件,例如:@ConditionalOnClass({ Undertow.class }),@Configuration(proxyBeanMethods = false)是封闭这个配备中 Bean 之间的署理加快加载速度。
由于每个配备都有@ConditionalOnMissingBean(ReactiveWebServerFactory.class),那么其实能保证就算满意多个配备的条件,最终也只需一个 ReactiveWebServerFactory,那么当满意多个条件时,哪个优先加载呢?这就要看这里的源码:
ReactiveWebServerFactoryAutoConfiguration
@Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,
    ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,
    ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,
    ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class })
拷贝代码 拷贝代码
复制代码
复制代码从这里能够看出,是依照EmbeddedTomcat,EmbeddedJetty,EmbeddedUndertow,EmbeddedNetty的顺序 Import 的,也就是:只需你的依靠中加入了任何 Web 容器(例如 Undertow),那么最终创立的就是依据那个 web 容器的异步容器,而不是依据 netty 的
为何 Web 容器换了就会有问题
首要, Spring Cloud Gateway
Spring Cloud Gateway requires the Netty runtime provided by Spring Boot and Spring Webflux. It does not work in a traditional Servlet Container or when built as a WAR.
就是 Spring Cloud Gateway 只能在 Netty 的环境中运转。这是为什么呢。当初在规划的时分,就假定了容器只能是 Netty,后续开发各种 Spring Cloud Gateway 的内置 Filter 以及 Filter 插件的时分,有许多假定其时就是 Netty 的代码,例如缓存 Body 的 Filter 运用的东西类ServerWebExchangeUtils:
private staticMonocacheRequestBody(ServerWebExchange exchange, boolean cacheDecoratedRequest,
      Function function) {
  ServerHttpResponse response = exchange.getResponse();
  //在这里,强制转换了 bufferFactory 为 NettyDataBufferFactory
  NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();
  // Join all the DataBuffers so we have a single DataBuffer for the body
  return DataBufferUtils.join(exchange.getRequest().getBody())
      .defaultIfEmpty(factory.wrap(new EmptyByteBuf(factory.getByteBufAllocator())))
      .map(dataBuffer -> decorate(exchange, dataBuffer, cacheDecoratedRequest))
      .switchIfEmpty(Mono.just(exchange.getRequest())).flatMap(function);
}
拷贝代码 拷贝代码
复制代码
复制代码从源码中能够看到,代码直接以为 response 中的 BufferFactory 就是 NettyDataBufferFactory,其实在其他 Web 容器的情况下,现在应该是 DefaultDataBufferFactory,这样就会有异常。不过在 v3.0.5 之后的版别,现已修复了这个强转









