# 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` 中注册。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-frameworks-88/spring/spring-webflux/webflux-de-zi-dong-zhuang-pei.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
