> For the complete documentation index, see [llms.txt](https://ldbmcs.gitbook.io/java/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ldbmcs.gitbook.io/java/java-frameworks-88/spring/spring-webflux/webflux-de-zi-dong-zhuang-pei.md).

# WebFlux的自动装配

根据之前的经验，咱使用WebFlux的时候，主启动类上相对比于WebMvc来讲没有任何区别，那只有自动配置类可以控制WebFlux的装配了，那装配的自动配置类不难猜想应该是：`WebFluxAutoConfiguration` 。

## 1. WebFluxAutoConfiguration

```java
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@ConditionalOnMissingBean({ WebFluxConfigurationSupport.class })
@AutoConfigureAfter({ ReactiveWebServerFactoryAutoConfiguration.class, CodecsAutoConfiguration.class,
                     ValidationAutoConfiguration.class })
                     @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
                     public class WebFluxAutoConfiguration
```

可以发现它跟 WebMvcAutoConfiguration 几乎没什么太大的区别：

```java
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
                     @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
                     @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
                     @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
                                          ValidationAutoConfiguration.class })
                                          public class WebMvcAutoConfiguration
```

只不过判断条件不太一样而已，WebFlux 需要判断的应用类型为 **REACTIVE** ，而 WebMvc 为 **SERVLET** ；WebFlux 需要判断classpath下是否有 `WebFluxConfigurer` 类，而 WebMvc 需要的是 `Servlet` 、`DispatcherServlet` 、`WebMvcConfigurer` 三个类。

根据之前读 WebMvc 的自动配置，肯定要先走进 `ReactiveWebServerFactoryAutoConfiguration` ，看一眼嵌入式容器的配置。

## 2. ReactiveWebServerFactoryAutoConfiguration

```java
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnClass(ReactiveHttpInputMessage.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
         ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,
         ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,
         ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,
         ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class })
public class ReactiveWebServerFactoryAutoConfiguration
```

果不其然，它会导入嵌入式容器工厂的配置类。由于在默认情况下，导入 `spring-boot-starter-webflux` ，默认使用 **Netty** 作为嵌入式容器，故此处 `EmbeddedNetty` 生效，生效的原因与之前 WebMvc 原理一致，不再赘述。

看一眼 `EmbeddedNetty` 都干了什么：

### 2.1 EmbeddedNetty

```java
@Configuration
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ HttpServer.class })
static class EmbeddedNetty {

    @Bean
    @ConditionalOnMissingBean
    public ReactorResourceFactory reactorServerResourceFactory() {
        return new ReactorResourceFactory();
    }

    @Bean
    public NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory) {
        NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
        serverFactory.setResourceFactory(resourceFactory);
        return serverFactory;
    }

}
```

很明显它创建了一个 `ReactorResourceFactory` ，一个 `NettyReactiveWebServerFactory` 。`NettyReactiveWebServerFactory` 从命名上看就知道它应该是类比于 WebMvc 中的 `TomcatServletWebServerFactory` ，那 `ReactorResourceFactory` 是什么呢？

### 2.2 ReactorResourceFactory

它的文档注释原文翻译：

Factory to manage Reactor Netty resources, i.e. LoopResources for event loop threads, and ConnectionProvider for the connection pool, within the lifecycle of a Spring ApplicationContext. This factory implements InitializingBean and DisposableBean and is expected typically to be declared as a Spring-managed bean.

在Spring `ApplicationContext` 的生命周期内，用于管理Reactor Netty资源的工厂，即用于事件循环线程的 `LoopResources` 和用于连接池的 `ConnectionProvider` 。

该工厂实现 `InitializingBean` 和 `DisposableBean`，通常应将其声明为Spring管理的Bean。

划重点：**管理Reactor Netty资源的工厂**，这个说法怎么感觉跟线程池似的？而且后面还有循环、连接池的概念，难不成它就类比于jdbc中的 `DataSource`？往里看它的成员：

```java
public class ReactorResourceFactory implements InitializingBean, DisposableBean {
    private boolean useGlobalResources = true;
    private Consumer<HttpResources> globalResourcesConsumer;
    private Supplier<ConnectionProvider> connectionProviderSupplier = () -> ConnectionProvider.elastic("webflux");
    private Supplier<LoopResources> loopResourcesSupplier = () -> LoopResources.create("webflux-http");
    // ......
```

注意第三个属性：`connectionProviderSupplier` ，它的创建方式是 `ConnectionProvider.elastic` ，突然感觉眼熟：前面看调度器的时候，对于 Reactor 的线程池就有一种 **elastic** 类型的！莫非它确实就是管理 `ConnectionProvider` 的？点开 ConnectionProvider 的 elastic 方法：

```java
static ConnectionProvider elastic(String name) {
    return new PooledConnectionProvider(name,
                                        (bootstrap, handler, checker) -> new SimpleChannelPool(bootstrap,
                                                                                               handler, checker, true, false));
```

果然它就是一个连接的提供者，而且它还是 **Pool** ，换句话说，咱就可以简单的理解成它是 **Reactor Netty 的连接池**。

实际Debug了一下，发现确实与咱的推测基本贴合：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673185326405-f90d94d6-f552-4941-9e5c-b77a6f15c62b.png)

果然有关于池的好多属性，而且下面有 **select** 和 **worker** 的概念，这就是 Netty 的核心。（12是因为我用的笔记本CPU是i7-8750H，6核12线程，所以这里是12）

***

`ReactiveWebServerFactoryAutoConfiguration` 看完之后，回到 `WebFluxAutoConfiguration`：

```java
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, CodecsAutoConfiguration.class,
                     ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration
```

它除了还要先处理 `ValidationAutoConfiguration` 的**JSR-303**校验之外，还要先处理一个 `CodecsAutoConfiguration`：

## 3. CodecsAutoConfiguration

可以发现这里面只是注册了一个 json 转换器，以及日志工具。源码很简单，不过多解析。

```java
@Configuration
@ConditionalOnClass(CodecConfigurer.class)
@AutoConfigureAfter(JacksonAutoConfiguration.class)
public class CodecsAutoConfiguration {

    private static final MimeType[] EMPTY_MIME_TYPES = {};

    @Configuration
    @ConditionalOnClass(ObjectMapper.class)
    static class JacksonCodecConfiguration {

        @Bean
        @Order(0)
        @ConditionalOnBean(ObjectMapper.class)
        public CodecCustomizer jacksonCodecCustomizer(ObjectMapper objectMapper) {
            return (configurer) -> {
                CodecConfigurer.DefaultCodecs defaults = configurer.defaultCodecs();
                defaults.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, EMPTY_MIME_TYPES));
                defaults.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, EMPTY_MIME_TYPES));
            };
        }

    }

    @Configuration
    @EnableConfigurationProperties(HttpProperties.class)
    static class LoggingCodecConfiguration {

        @Bean
        @Order(0)
        public CodecCustomizer loggingCodecCustomizer(HttpProperties properties) {
            return (configurer) -> configurer.defaultCodecs()
                .enableLoggingRequestDetails(properties.isLogRequestDetails());
        }

    }

}
```

接下来才是最核心的 `WebFluxAutoConfiguration` 。源码中它定义了三个内部类，咱一个一个来看：

## 4. WebFluxConfig

```java
@Configuration
@EnableConfigurationProperties({ ResourceProperties.class, WebFluxProperties.class })
@Import({ EnableWebFluxConfiguration.class })
public static class WebFluxConfig implements WebFluxConfigurer
```

可以看到它又导入了一个 `EnableWebFluxConfiguration` ，而它就是下面第5章节的 `EnableWebFluxConfiguration` ，咱从上往下一样一样看。先看 `WebFluxConfig` 中的配置：

### 4.1 静态资源映射

```java
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    if (!registry.hasMappingForPattern("/webjars/**")) {
        ResourceHandlerRegistration registration = registry.addResourceHandler("/webjars/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/");
        configureResourceCaching(registration);
        customizeResourceHandlerRegistration(registration);
    }
    String staticPathPattern = this.webFluxProperties.getStaticPathPattern();
    if (!registry.hasMappingForPattern(staticPathPattern)) {
        ResourceHandlerRegistration registration = registry.addResourceHandler(staticPathPattern)
            .addResourceLocations(this.resourceProperties.getStaticLocations());
        configureResourceCaching(registration);
        customizeResourceHandlerRegistration(registration);
    }
}
```

可以发现它处理的逻辑几乎跟 WebMvc 部分一致！也是处理 webjars 的资源，以及 ResourceProperties 中的静态路径，默认情况下：

```java
public class ResourceProperties {
    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
                                                                  "classpath:/resources/", "classpath:/static/", "classpath:/public/" };
    private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
```

发现也是跟 WebMvc 部分一样的路径。

### 4.2 ViewResolver

```java
public void configureViewResolvers(ViewResolverRegistry registry) {
    this.viewResolvers.orderedStream().forEach(registry::viewResolver);
}
```

可以发现这部分是配置 `ViewResolver` 的，不过默认情况下Debug发现并没有进入 `ViewResolverRegistry` 的 `registry` 方法中，暂且略过。

### 4.3 Converter和Formatter

```java
public void addFormatters(FormatterRegistry registry) {
    for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
        registry.addConverter(converter);
    }
    for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
        registry.addConverter(converter);
    }
    for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
        registry.addFormatter(formatter);
    }
}
```

这部分在 WebMvc 部分也是一模一样的，直接copy过来的！（不过这部分不是特别关键，而且之前也没有单独拿出来聊，小伙伴们知道这里是配置转换器的即可）

大概来看 `WebFluxConfig` 主要就配置了这几个组件，继续往下看：

## 5. EnableWebFluxConfiguration

先看一眼继承：

```java
@Configuration
public static class EnableWebFluxConfiguration extends DelegatingWebFluxConfiguration
```

它继承了 `DelegatingWebFluxConfiguration` ，这个套路貌似跟 WebMvc 部分也是一样的！

```java
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware
```

至于这些 `Delegating***Configuration` 的作用咱之前也提到过，它就是 `@EnableWebMvc` 或者 `@EnableWebFlux` 注解导入的配置类：

```java
@Import(DelegatingWebFluxConfiguration.class)
public @interface EnableWebFlux
```

它的作用小伙伴们还记得吗？只要在 SpringBoot 中标注了这样的注解，代表 SpringBoot 默认的自动配置类不生效，改由咱们自己接管配置 WebMvc 或者 WebFlux 。

下面来看它里面配置的组件：

### 5.1 FormattingConversionService

```java
@Bean
public FormattingConversionService webFluxConversionService() {
    WebConversionService conversionService = new WebConversionService(this.webFluxProperties.getDateFormat());
    addFormatters(conversionService);
    return conversionService;
}
```

看这个类的名，大概也能联想到之前看 WebMvc 部分的那个参数类型转换器吧！而且代码几乎也一模一样。

### 5.2 Validator

```java
@Bean
public Validator webFluxValidator() {
    if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
        return super.webFluxValidator();
    }
    return ValidatorAdapter.get(getApplicationContext(), getValidator());
}
```

很明显它是配置 **JSR-303** 参数校验的校验器。

### 5.3 HandlerMapping和HandlerAdapter

```java
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
    if (this.webFluxRegistrations != null
        && this.webFluxRegistrations.getRequestMappingHandlerAdapter() != null) {
        return this.webFluxRegistrations.getRequestMappingHandlerAdapter();
    }
    return super.createRequestMappingHandlerAdapter();
}

protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
    if (this.webFluxRegistrations != null
        && this.webFluxRegistrations.getRequestMappingHandlerMapping() != null) {
        return this.webFluxRegistrations.getRequestMappingHandlerMapping();
    }
    return super.createRequestMappingHandlerMapping();
}
```

哇塞这不是咱之前在 WebMvc 部分常聊的两个配合 `DispatcherServlet` 的核心组件吗？对的，它在 WebFlux 中也是一样的其效果。

## 6. WebFluxConfigurationSupport

上面咱注意到了 `EnableWebFluxConfiguration` 继承了 `DelegatingWebFluxConfiguration`，而它又继承了 `WebFluxConfigurationSupport` ，这个配置类中还注册了一些组件：

### 6.1 DispatcherHandler

```java
@Bean
public DispatcherHandler webHandler() {
    return new DispatcherHandler();
}
```

发现了 WebFlux 的核心前端控制器：`**DispatcherHandler**` ，它在这里注册了，而且比 `DispatcherServlet` 简单的多。

### 6.2 WebExceptionHandler

```java
@Bean
@Order(0)
public WebExceptionHandler responseStatusExceptionHandler() {
    return new WebFluxResponseStatusExceptionHandler();
}
```

WebFlux 的异常状态响应处理器，见名知意，不再深扒。

### 6.3 RequestMappingHandlerMapping

```java
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    mapping.setOrder(0);
    mapping.setContentTypeResolver(webFluxContentTypeResolver());
    mapping.setCorsConfigurations(getCorsConfigurations());

    PathMatchConfigurer configurer = getPathMatchConfigurer();
    Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
    if (useTrailingSlashMatch != null) {
        mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
    }
    Boolean useCaseSensitiveMatch = configurer.isUseCaseSensitiveMatch();
    if (useCaseSensitiveMatch != null) {
        mapping.setUseCaseSensitiveMatch(useCaseSensitiveMatch);
    }
    Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
    if (pathPrefixes != null) {
        mapping.setPathPrefixes(pathPrefixes);
    }

    return mapping;
}
```

可以发现这里真正创建了 `RequestMappingHandlerMapping` 组件。

### 6.4 RouterFunctionMapping

```java
@Bean
public RouterFunctionMapping routerFunctionMapping() {
    RouterFunctionMapping mapping = createRouterFunctionMapping();
    mapping.setOrder(-1); // go before RequestMappingHandlerMapping
    mapping.setMessageReaders(serverCodecConfigurer().getReaders());
    mapping.setCorsConfigurations(getCorsConfigurations());

    return mapping;
}
```

与 `RequestMappingHandlerMapping` 区别开来，它是**函数式端点路由编程的Mapping处理器**。至于它的作用，咱到第33篇再聊。

### 6.5 SimpleUrlHandlerMapping

```java
@Bean
public HandlerMapping resourceHandlerMapping() {
    ResourceLoader resourceLoader = this.applicationContext;
    if (resourceLoader == null) {
        resourceLoader = new DefaultResourceLoader();
    }
    ResourceHandlerRegistry registry = new ResourceHandlerRegistry(resourceLoader);
    registry.setResourceUrlProvider(resourceUrlProvider());
    addResourceHandlers(registry);

    AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
    if (handlerMapping != null) {
        PathMatchConfigurer configurer = getPathMatchConfigurer();
        Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
        Boolean useCaseSensitiveMatch = configurer.isUseCaseSensitiveMatch();
        if (useTrailingSlashMatch != null) {
            handlerMapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
        }
        if (useCaseSensitiveMatch != null) {
            handlerMapping.setUseCaseSensitiveMatch(useCaseSensitiveMatch);
        }
    }
    else {
        handlerMapping = new EmptyHandlerMapping();
    }
    return handlerMapping;
}
```

注意看源码中第一个if结构下面，它用了一个 `ResourceHandlerRegistry` ，有没有感觉似曾相识？咱在前面看静态资源映射的时候见过它，它是**处理静态资源的映射**的。通常情况下咱的项目中会有一些静态资源，只要存在静态资源，它就会创建一个 `SimpleUrlHandlerMapping` 来真正处理静态资源的路径映射。通过Debug，发现确实存在（因为有应用图标 `favicon.ico`）：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673185602063-dbb7a140-27f2-4ad1-89d6-7d75fb29e3b3.png)

### 6.6 RequestMappingHandlerAdapter

```java
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
    adapter.setMessageReaders(serverCodecConfigurer().getReaders());
    adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
    adapter.setReactiveAdapterRegistry(webFluxAdapterRegistry());

    ArgumentResolverConfigurer configurer = new ArgumentResolverConfigurer();
    configureArgumentResolvers(configurer);
    adapter.setArgumentResolverConfigurer(configurer);

    return adapter;
}
```

这里真正创建了 `RequestMappingHandlerAdapter` 。

### 6.7 LocaleContextResolver

```java
@Bean
public LocaleContextResolver localeContextResolver() {
    return createLocaleContextResolver();
}
```

这个 `LocaleContextResolver` 组件从类名上就可以看出来它是与国际化相关的组件。

### 6.8 ReactiveAdapterRegistry

```java
@Bean
public ReactiveAdapterRegistry webFluxAdapterRegistry() {
    return new ReactiveAdapterRegistry();
}
```

这个 `ReactiveAdapterRegistry` 类看上去应该是处理 **Reactive** 类型的，看一眼它的文档注释：

A registry of adapters to adapt Reactive Streams Publisher to/from various async/reactive types such as CompletableFuture, RxJava Observable, and others. By default, depending on classpath availability, adapters are registered for Reactor, RxJava 1, RxJava 2 types, CompletableFuture, and Java 9+ Flow\.Publisher.

适配器注册表，用于使Reactive Streams Publisher适应各种异步/反应类型，例如CompletableFuture，RxJava Observable等。 默认情况下，根据类路径的可用性，为Reactor，RxJava 1，RxJava 2类型，CompletableFuture和Java 9+ Flow\.Publisher注册适配器。

果然，它可以处理多种 **Reactive Stream** 的发布器，它提到了 Reactor 、RxJava 、jdk9版本的 `Flow` 等。

### 6.9 一组ResultHandler

```java
@Bean // 处理HttpEntity和ResponseEntity
public ResponseEntityResultHandler responseEntityResultHandler() {
    return new ResponseEntityResultHandler(serverCodecConfigurer().getWriters(),
                                           webFluxContentTypeResolver(), webFluxAdapterRegistry());
}

@Bean // 处理@ResponseBody类型的
public ResponseBodyResultHandler responseBodyResultHandler() {
    return new ResponseBodyResultHandler(serverCodecConfigurer().getWriters(),
                                         webFluxContentTypeResolver(), webFluxAdapterRegistry());
}

@Bean // 返回视图类型
public ViewResolutionResultHandler viewResolutionResultHandler() {
    ViewResolverRegistry registry = getViewResolverRegistry();
    List<ViewResolver> resolvers = registry.getViewResolvers();
    ViewResolutionResultHandler handler = new ViewResolutionResultHandler(
        resolvers, webFluxContentTypeResolver(), webFluxAdapterRegistry());
    handler.setDefaultViews(registry.getDefaultViews());
    handler.setOrder(registry.getOrder());
    return handler;
}

@Bean // 处理返回值类型为ServerResponse
public ServerResponseResultHandler serverResponseResultHandler() {
    List<ViewResolver> resolvers = getViewResolverRegistry().getViewResolvers();
    ServerResponseResultHandler handler = new ServerResponseResultHandler();
    handler.setMessageWriters(serverCodecConfigurer().getWriters());
    handler.setViewResolvers(resolvers);
    return handler;
}
```

WebFlux 提供了4种 `ResultHandler` ，每种功能已标注在源码中。

## 7. ResourceChainCustomizerConfiguration

在最底下还有一个，不过它的配置很简单：

```java
@Configuration
@ConditionalOnEnabledResourceChain
static class ResourceChainCustomizerConfiguration {
    @Bean
    public ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
        return new ResourceChainResourceHandlerRegistrationCustomizer();
    }
}
```

发现它有一个 `@ConditionalOnEnableResourceChain` 注解，它的作用咱不深追究了，它实际是跟一个 `application.properties` 中的配置有关：`spring.resources.chain.strategy.fixed.enabled` ，如果它配置为true，这个条件才生效，默认不生效，不再深究。

## 8. 小结

1. `WebFluxAutoConfiguration` 的配置整体与 `WebMvcAutoConfiguration` 非常相似，其中不乏包括几个核心组件。
2. `WebFluxAutoConfiguration` 默认配置的Web容器是Netty而非 Tomcat 等传统 Servlet 容器。
3. 大部分比较熟悉的组件都在 `DelegatingWebFluxConfiguration` 的父类 `WebFluxConfigurationSupport` 中注册。
