Spring WebFlux 实现原理与架构图

启动原理与架构图

通过spring-boot-autoconfigure中的spring.factories文件,通过Spring Boot自动初始化下列类:HttpHandlerAutoConfiguration、ReactiveWebServerFactoryAutoConfiguration、WebFluxAutoConfiguration、ErrorWebFluxAutoConfiguration、ClientHttpConnectorAutoConfiguration、WebClientAutoConfiguration。

WebFluxAutoConfiguration

通过WebFluxConfig完成需要类的初始化,是通过EnableWebFluxConfiguration类完成,该类继承了WebFluxConfigurationSupport。

HttpHandlerAutoConfiguration

完成了将DispatcherHandler 和List<WebFilter>注入到DefaultWebFilterChain(该类实现了WebFilterChain)过滤链。形成了先执行WebFliter在执行DispatchHandler,并通过通过Mono.defer订阅Reactor服务。完成操作后通过HttpWebHandlerAdapter完成将HttpHandler转为WebHandler可执行的类。

架构图如下

在这里插入图片描述

WebFluxConfigurationSupport

初始化Bean

DispatcherHandler

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

HandlerMapping

RequestMappingHandlerMapping

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();mapping.setOrder(0);mapping.setContentTypeResolver(contentTypeResolver);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;
}

RouterFunctionMapping

@Bean
public RouterFunctionMapping routerFunctionMapping(ServerCodecConfigurer serverCodecConfigurer) {RouterFunctionMapping mapping = createRouterFunctionMapping();mapping.setOrder(-1);  // go before RequestMappingHandlerMappingmapping.setMessageReaders(serverCodecConfigurer.getReaders());mapping.setCorsConfigurations(getCorsConfigurations());return mapping;
}

SimpleUrlHandlerMapping

@Bean
public HandlerMapping resourceHandlerMapping(ResourceUrlProvider resourceUrlProvider) {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;
}@Bean
public ResourceUrlProvider resourceUrlProvider() {return new ResourceUrlProvider();
}

HandlerAdapter

RequestMappingHandlerAdapter

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,ServerCodecConfigurer serverCodecConfigurer,@Qualifier("webFluxConversionService") FormattingConversionService conversionService,@Qualifier("webFluxValidator") Validator validator) {RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();adapter.setMessageReaders(serverCodecConfigurer.getReaders());adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));adapter.setReactiveAdapterRegistry(reactiveAdapterRegistry);ArgumentResolverConfigurer configurer = new ArgumentResolverConfigurer();configureArgumentResolvers(configurer);adapter.setArgumentResolverConfigurer(configurer);return adapter;
}

HandlerFunctionAdapter

@Bean
public HandlerFunctionAdapter handlerFunctionAdapter() {return new HandlerFunctionAdapter();
}

SimpleHandlerAdapter

@Bean
public SimpleHandlerAdapter simpleHandlerAdapter() {return new SimpleHandlerAdapter();
}

HandlerResultHandler

ResponseEntityResultHandler

@Bean
public ResponseEntityResultHandler responseEntityResultHandler(@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,ServerCodecConfigurer serverCodecConfigurer,@Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {return new ResponseEntityResultHandler(serverCodecConfigurer.getWriters(),contentTypeResolver, reactiveAdapterRegistry);
}

ResponseBodyResultHandler

@Bean
public ResponseBodyResultHandler responseBodyResultHandler(@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,ServerCodecConfigurer serverCodecConfigurer,@Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {return new ResponseBodyResultHandler(serverCodecConfigurer.getWriters(),contentTypeResolver, reactiveAdapterRegistry);
}

ViewResolutionResultHandler

@Bean
public ViewResolutionResultHandler viewResolutionResultHandler(@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,@Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {ViewResolverRegistry registry = getViewResolverRegistry();List<ViewResolver> resolvers = registry.getViewResolvers();ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, contentTypeResolver, reactiveAdapterRegistry);handler.setDefaultViews(registry.getDefaultViews());handler.setOrder(registry.getOrder());return handler;
}

ServerResponseResultHandler

@Bean
public ServerResponseResultHandler serverResponseResultHandler(ServerCodecConfigurer serverCodecConfigurer) {List<ViewResolver> resolvers = getViewResolverRegistry().getViewResolvers();ServerResponseResultHandler handler = new ServerResponseResultHandler();handler.setMessageWriters(serverCodecConfigurer.getWriters());handler.setViewResolvers(resolvers);return handler;
}

DelegatingWebFluxConfiguration

继承WebFluxConfigurationSupport

EnableWebFluxConfiguration

HttpHandler

主要执行方法

Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response);

ContextPathCompositeHandler

HttpWebHandlerAdapter

继承了WebHandlerDecorator,,实现了HttpHandler
1.通过createExchange方法将ServerHttpRequest和ServerHttpResponse转换为ServerWebExchange供应WebHandler使用。
2.通过代理执行WebHandler的方法。
3.处理异常请求
4.成功返回。

public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {if (this.forwardedHeaderTransformer != null) {try {request = this.forwardedHeaderTransformer.apply(request);} catch (Throwable var4) {if (logger.isDebugEnabled()) {logger.debug("Failed to apply forwarded headers to " + this.formatRequest(request), var4);}response.setStatusCode(HttpStatus.BAD_REQUEST);return response.setComplete();}}ServerWebExchange exchange = this.createExchange(request, response);LogFormatUtils.traceDebug(logger, (traceOn) -> {return exchange.getLogPrefix() + this.formatRequest(exchange.getRequest()) + (traceOn ? ", headers=" + this.formatHeaders(exchange.getRequest().getHeaders()) : "");});Mono var10000 = this.getDelegate().handle(exchange).doOnSuccess((aVoid) -> {this.logResponse(exchange);}).onErrorResume((ex) -> {return this.handleUnresolvedError(exchange, ex);});response.getClass();return var10000.then(Mono.defer(response::setComplete));
}

将ServerHttpRequest和ServerHttpResponse转换为ServerWebExchange

protected ServerWebExchange createExchange(ServerHttpRequest request, ServerHttpResponse response) {return new DefaultServerWebExchange(request, response, this.sessionManager, this.getCodecConfigurer(), this.getLocaleContextResolver(), this.applicationContext);
}

WebHandler

Mono<Void> handle(ServerWebExchange exchange);

FilteringWebHandler()

主要用于Gateway中过滤链的创建

private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {return (List)filters.stream().map((filter) -> {GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);if (filter instanceof Ordered) {int order = ((Ordered)filter).getOrder();return new OrderedGatewayFilter(gatewayFilter, order);} else {return gatewayFilter;}}).collect(Collectors.toList());
}public Mono<Void> handle(ServerWebExchange exchange) {Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);List<GatewayFilter> gatewayFilters = route.getFilters();List<GatewayFilter> combined = new ArrayList(this.globalFilters);combined.addAll(gatewayFilters);AnnotationAwareOrderComparator.sort(combined);if (logger.isDebugEnabled()) {logger.debug("Sorted gatewayFilterFactories: " + combined);}return (new DefaultGatewayFilterChain(combined)).filter(exchange);
}

ResourceWebHandler

处理资源请求。
检查配置的位置列表中是否存在请求的资源。如果资源不存在,将向客户端返回404响应。如果资源存在,则将检查请求是否存在Last Modified标头,并将其值与给定资源的最后修改的时间戳进行比较,如果Last Modified值更大,则返回304状态代码。如果资源比“上次修改”的值新,或者标头不存在,则资源的内容资源将写入到响应中,缓存标头将在一年后过期。

@Override
public Mono<Void> handle(ServerWebExchange exchange) {return getResource(exchange).switchIfEmpty(Mono.defer(() -> {logger.debug(exchange.getLogPrefix() + "Resource not found");return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND));})).flatMap(resource -> {try {if (HttpMethod.OPTIONS.matches(exchange.getRequest().getMethodValue())) {exchange.getResponse().getHeaders().add("Allow", "GET,HEAD,OPTIONS");return Mono.empty();}// Supported methods and required sessionHttpMethod httpMethod = exchange.getRequest().getMethod();if (!SUPPORTED_METHODS.contains(httpMethod)) {return Mono.error(new MethodNotAllowedException(exchange.getRequest().getMethodValue(), SUPPORTED_METHODS));}// Header phaseif (exchange.checkNotModified(Instant.ofEpochMilli(resource.lastModified()))) {logger.trace(exchange.getLogPrefix() + "Resource not modified");return Mono.empty();}// Apply cache settings, if anyCacheControl cacheControl = getCacheControl();if (cacheControl != null) {exchange.getResponse().getHeaders().setCacheControl(cacheControl);}// Check the media type for the resourceMediaType mediaType = MediaTypeFactory.getMediaType(resource).orElse(null);setHeaders(exchange, resource, mediaType);// Content phaseResourceHttpMessageWriter writer = getResourceHttpMessageWriter();Assert.state(writer != null, "No ResourceHttpMessageWriter");return writer.write(Mono.just(resource),null, ResolvableType.forClass(Resource.class), mediaType,exchange.getRequest(), exchange.getResponse(),Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()));}catch (IOException ex) {return Mono.error(ex);}});
}

DispatcherHandler

@Nullable
private List<HandlerMapping> handlerMappings;@Nullable
private List<HandlerAdapter> handlerAdapters;@Nullable
private List<HandlerResultHandler> resultHandlers;protected void initStrategies(ApplicationContext context) {Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());AnnotationAwareOrderComparator.sort(mappings);this.handlerMappings = Collections.unmodifiableList(mappings);Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);this.handlerAdapters = new ArrayList<>(adapterBeans.values());AnnotationAwareOrderComparator.sort(this.handlerAdapters);Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);this.resultHandlers = new ArrayList<>(beans.values());AnnotationAwareOrderComparator.sort(this.resultHandlers);
}

每次执行请求,都会到当前方法中
1.根据ServerWebExchange获取到对应的HandlerMapping。
2.在从HandlerMapping中获取到对应的Handler。
3.根据Handler选取对应的HandlerAdapter。由HandlerAdapter执行方法。
4.根据执行结果,选择对应的HandlerResultHandler。由其处理结果返回值。

@Override
public Mono<Void> handle(ServerWebExchange exchange) {if (this.handlerMappings == null) {return createNotFoundError();}return Flux.fromIterable(this.handlerMappings).concatMap(mapping -> mapping.getHandler(exchange)).next().switchIfEmpty(createNotFoundError()).flatMap(handler -> invokeHandler(exchange, handler)).flatMap(result -> handleResult(exchange, result));
}

HandlerMapping(处理器Mapping)

返回此请求的处理器

Mono<Object> getHandler(ServerWebExchange exchange);

AbstractHandlerMapping(抽象处理器Mapping)

getHandler:根据请求获取到对应的Handler
1.通过getHandlerInternal方法获取到对应的控制器。
2.在通过map函数判断请求是否保护CORS,如则对Handler添加对应的信息。

@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {return getHandlerInternal(exchange).map(handler -> {if (logger.isDebugEnabled()) {logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);}ServerHttpRequest request = exchange.getRequest();if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null);CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);config = (config != null ? config.combine(handlerConfig) : handlerConfig);if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {return REQUEST_HANDLED_HANDLER;}}return handler;});
}
AbstractHandlerMethodMapping

1.启动读锁,防止多线程执行时候出现数据异常
2.通过lookuphandlerMethod方法获取HandlerMethod
3.释放读锁

@Override
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {this.mappingRegistry.acquireReadLock();try {HandlerMethod handlerMethod;try {handlerMethod = lookupHandlerMethod(exchange);}catch (Exception ex) {return Mono.error(ex);}if (handlerMethod != null) {handlerMethod = handlerMethod.createWithResolvedBean();}return Mono.justOrEmpty(handlerMethod);}finally {this.mappingRegistry.releaseReadLock();}
}

1.通过addMatchingMappings方法,获取到对应匹配的数据
2.

@Nullable
protected HandlerMethod lookupHandlerMethod(ServerWebExchange exchange) throws Exception {List<Match> matches = new ArrayList<>();addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, exchange);if (!matches.isEmpty()) {Comparator<Match> comparator = new MatchComparator(getMappingComparator(exchange));matches.sort(comparator);Match bestMatch = matches.get(0);if (matches.size() > 1) {if (logger.isTraceEnabled()) {logger.trace(exchange.getLogPrefix() + matches.size() + " matching mappings: " + matches);}if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {return PREFLIGHT_AMBIGUOUS_MATCH;}Match secondBestMatch = matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {Method m1 = bestMatch.handlerMethod.getMethod();Method m2 = secondBestMatch.handlerMethod.getMethod();RequestPath path = exchange.getRequest().getPath();throw new IllegalStateException("Ambiguous handler methods mapped for '" + path + "': {" + m1 + ", " + m2 + "}");}}handleMatch(bestMatch.mapping, bestMatch.handlerMethod, exchange);return bestMatch.handlerMethod;}else {return handleNoMatch(this.mappingRegistry.getMappings().keySet(), exchange);}
}/**
* 新增匹配Mapping 
*/
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, ServerWebExchange exchange) {for (T mapping : mappings) {T match = getMatchingMapping(mapping, exchange);if (match != null) {matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));}}
}

Handler Methods 发现
1.由InitializingBean执行AfetrPropertiesSet方法
2.获取上下文中所有Bean名称
3.如果Bean名称的开头是否是”scopedTarget.”
4.如果是则执行handlerMethodsInitialzed,本方式是一个空方法。
5.如果不是则,则从上下文中获取对应类。
6.通过isHandler()方法判断类是否是Handler类型,这里在类RequeMappingHandlerMapping中判断逻辑是注解@RequestMapping或是@Controller
7.通过类返射,将所有方法和对应的关系都注册到MappingRegistry

public void afterPropertiesSet() {initHandlerMethods();// Total includes detected mappings + explicit registrations via registerMapping..int total = this.getHandlerMethods().size();if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {logger.debug(total + " mappings in " + formatMappingName());}
}protected void initHandlerMethods() {String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class);for (String beanName : beanNames) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {Class<?> beanType = null;try {beanType = obtainApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isTraceEnabled()) {logger.trace("Could not resolve type for bean '" + beanName + "'", ex);}}if (beanType != null && isHandler(beanType)) {detectHandlerMethods(beanName);}}}handlerMethodsInitialized(getHandlerMethods());
}protected void detectHandlerMethods(final Object handler) {Class<?> handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass());if (handlerType != null) {final Class<?> userType = ClassUtils.getUserClass(handlerType);Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType));if (logger.isTraceEnabled()) {logger.trace(formatMappings(userType, methods));}methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);registerHandlerMethod(handler, invocableMethod, mapping);});}
}
RequestMappingInfoHandlerMapping

1.通过RequestMappingInfo中获取到指定的RequeMappingInfo

@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, ServerWebExchange exchange) {return info.getMatchingCondition(exchange);
}

1.请求方法不能为OPTION或空
2.比对请求参数,是否匹配。 默认是不匹配。
3.请求头中,是否包含指定的头内容(去除Accept, Content-Type) 默认不匹配
4.请求头Content-Type中设置的类型是否支持,默认支持所有
5.支持的媒体类型,默认支持所有。
6.请求路径匹配,当前请求路径和实现的请求路径匹配
7.请求持有者判断,默认不进行。
8.当上述条件都满足时,创建RequestMappingInfo对象。

public RequestMappingInfo getMatchingCondition(ServerWebExchange exchange) {RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(exchange);if (methods == null) {return null;}ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(exchange);if (params == null) {return null;}HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(exchange);if (headers == null) {return null;}// Match "Content-Type" and "Accept" (parsed ones and cached) before patternsConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(exchange);if (consumes == null) {return null;}ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(exchange);if (produces == null) {return null;}PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(exchange);if (patterns == null) {return null;}RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(exchange);if (custom == null) {return null;}return new RequestMappingInfo(this.name, patterns,methods, params, headers, consumes, produces, custom.getCondition());
}
RequestMappingHandlerMapping
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);RequestCondition<?> condition = (element instanceof Class ?getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {RequestMappingInfo.Builder builder = RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name());if (customCondition != null) {builder.customCondition(customCondition);}return builder.options(this.config).build();
}

判断是否是Handler

@Override
protected boolean isHandler(Class<?> beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
AbstractUrlHandlerMapping
@Override
public Mono<Object> getHandlerInternal(ServerWebExchange exchange) {PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();Object handler;try {handler = lookupHandler(lookupPath, exchange);}catch (Exception ex) {return Mono.error(ex);}return Mono.justOrEmpty(handler);
}protected Object lookupHandler(PathContainer lookupPath, ServerWebExchange exchange) throws Exception {List<PathPattern> matches = this.handlerMap.keySet().stream().filter(key -> key.matches(lookupPath)).collect(Collectors.toList());if (matches.isEmpty()) {return null;}if (matches.size() > 1) {matches.sort(PathPattern.SPECIFICITY_COMPARATOR);if (logger.isTraceEnabled()) {logger.debug(exchange.getLogPrefix() + "Matching patterns " + matches);}}PathPattern pattern = matches.get(0);PathContainer pathWithinMapping = pattern.extractPathWithinPattern(lookupPath);return handleMatch(this.handlerMap.get(pattern), pattern, pathWithinMapping, exchange);
}private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer pathWithinMapping,ServerWebExchange exchange) {// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = obtainApplicationContext().getBean(handlerName);}validateHandler(handler, exchange);exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch);exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);return handler;
}

注册Handler

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {Assert.notNull(urlPath, "URL path must not be null");Assert.notNull(handler, "Handler object must not be null");Object resolvedHandler = handler;// Parse path patternurlPath = prependLeadingSlash(urlPath);PathPattern pattern = getPathPatternParser().parse(urlPath);if (this.handlerMap.containsKey(pattern)) {Object existingHandler = this.handlerMap.get(pattern);if (existingHandler != null && existingHandler != resolvedHandler) {throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to [" + urlPath + "]: " +"there is already " + getHandlerDescription(existingHandler) + " mapped.");}}// Eagerly resolve handler if referencing singleton via name.if (!this.lazyInitHandlers && handler instanceof String) {String handlerName = (String) handler;if (obtainApplicationContext().isSingleton(handlerName)) {resolvedHandler = obtainApplicationContext().getBean(handlerName);}}// Register resolved handlerthis.handlerMap.put(pattern, resolvedHandler);if (logger.isTraceEnabled()) {logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));}
}
SimpleUrlHandlerMapping

初始化,并注册Handler

@Override
public void initApplicationContext() throws BeansException {super.initApplicationContext();registerHandlers(this.urlMap);
}protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {if (urlMap.isEmpty()) {logger.trace("No patterns in " + formatMappingName());}else {for (Map.Entry<String, Object> entry : urlMap.entrySet()) {String url = entry.getKey();Object handler = entry.getValue();// Prepend with slash if not already present.if (!url.startsWith("/")) {url = "/" + url;}// Remove whitespace from handler bean name.if (handler instanceof String) {handler = ((String) handler).trim();}registerHandler(url, handler);}if (logger.isDebugEnabled()) {logger.debug("Patterns " + getHandlerMap().keySet() + " in " + formatMappingName());}}
}
RouterFunctionMapping

初始化执行
1.由InitializingBean执行AfetrPropertiesSet方法
2.如果消息读物为空,通过ServerCodeConfigurer类创建
3.如果routerFunction(路由方法),为空,则通过initRouteFunctions方法创建
4.通过routerFunctions方法执行Lambda,从上下文件中获取Bean类型为RouterFunction.class类型的Bean。

@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() {List<RouterFunction<?>> routerFunctions = routerFunctions();this.routerFunction = routerFunctions.stream().reduce(RouterFunction::andOther).orElse(null);logRouterFunctions(routerFunctions);
}private List<RouterFunction<?>> routerFunctions() {List<RouterFunction<?>> functions = obtainApplicationContext().getBeanProvider(RouterFunction.class).orderedStream().map(router -> (RouterFunction<?>)router).collect(Collectors.toList());return (!CollectionUtils.isEmpty(functions) ? functions : Collections.emptyList());
}

获取执行方法

@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {if (this.routerFunction != null) {ServerRequest request = ServerRequest.create(exchange, this.messageReaders);return this.routerFunction.route(request).doOnNext(handler -> setAttributes(exchange.getAttributes(), request, handler));}else {return Mono.empty();}
}

HandlerAdapter

是否执行当前Handler

boolean supports(Object handler);

执行Handler并返回结果

Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler);

WebSocketHandlerAdapter

SimpleHandlerAdapter

RequestMappingHandlerAdapter

执行执行的Handler

public boolean supports(Object handler) {return handler instanceof HandlerMethod;
}

执行

@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {HandlerMethod handlerMethod = (HandlerMethod) handler;Assert.state(this.methodResolver != null && this.modelInitializer != null, "Not initialized");InitBinderBindingContext bindingContext = new InitBinderBindingContext(getWebBindingInitializer(), this.methodResolver.getInitBinderMethods(handlerMethod));InvocableHandlerMethod invocableMethod = this.methodResolver.getRequestMappingMethod(handlerMethod);Function<Throwable, Mono<HandlerResult>> exceptionHandler =ex -> handleException(ex, handlerMethod, bindingContext, exchange);return this.modelInitializer.initModel(handlerMethod, bindingContext, exchange).then(Mono.defer(() -> invocableMethod.invoke(exchange, bindingContext))).doOnNext(result -> result.setExceptionHandler(exceptionHandler)).doOnNext(result -> bindingContext.saveModel()).onErrorResume(exceptionHandler);
}

HandlerFunctionAdapter

HandlerResultHandler

ServerResponseResultHandler(响应Server)

ResponseBodyResultHandler(响应Body)

ViewResolutionResultHandler(视图解析)

public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {Mono<Object> valueMono;ResolvableType valueType;ReactiveAdapter adapter = getAdapter(result);if (adapter != null) {if (adapter.isMultiValue()) {throw new IllegalArgumentException("Multi-value reactive types not supported in view resolution: " + result.getReturnType());}valueMono = (result.getReturnValue() != null ?Mono.from(adapter.toPublisher(result.getReturnValue())) : Mono.empty());valueType = (adapter.isNoValue() ? ResolvableType.forClass(Void.class) :result.getReturnType().getGeneric());}else {valueMono = Mono.justOrEmpty(result.getReturnValue());valueType = result.getReturnType();}return valueMono.switchIfEmpty(exchange.isNotModified() ? Mono.empty() : NO_VALUE_MONO).flatMap(returnValue -> {Mono<List<View>> viewsMono;Model model = result.getModel();MethodParameter parameter = result.getReturnTypeSource();Locale locale = LocaleContextHolder.getLocale(exchange.getLocaleContext());Class<?> clazz = valueType.toClass();if (clazz == Object.class) {clazz = returnValue.getClass();}if (returnValue == NO_VALUE || clazz == void.class || clazz == Void.class) {viewsMono = resolveViews(getDefaultViewName(exchange), locale);}else if (CharSequence.class.isAssignableFrom(clazz) && !hasModelAnnotation(parameter)) {viewsMono = resolveViews(returnValue.toString(), locale);}else if (Rendering.class.isAssignableFrom(clazz)) {Rendering render = (Rendering) returnValue;HttpStatus status = render.status();if (status != null) {exchange.getResponse().setStatusCode(status);}exchange.getResponse().getHeaders().putAll(render.headers());model.addAllAttributes(render.modelAttributes());Object view = render.view();if (view == null) {view = getDefaultViewName(exchange);}viewsMono = (view instanceof String ? resolveViews((String) view, locale) :Mono.just(Collections.singletonList((View) view)));}else if (Model.class.isAssignableFrom(clazz)) {model.addAllAttributes(((Model) returnValue).asMap());viewsMono = resolveViews(getDefaultViewName(exchange), locale);}else if (Map.class.isAssignableFrom(clazz) && !hasModelAnnotation(parameter)) {model.addAllAttributes((Map<String, ?>) returnValue);viewsMono = resolveViews(getDefaultViewName(exchange), locale);}else if (View.class.isAssignableFrom(clazz)) {viewsMono = Mono.just(Collections.singletonList((View) returnValue));}else {String name = getNameForReturnValue(parameter);model.addAttribute(name, returnValue);viewsMono = resolveViews(getDefaultViewName(exchange), locale);}BindingContext bindingContext = result.getBindingContext();updateBindingResult(bindingContext, exchange);return viewsMono.flatMap(views -> render(views, model.asMap(), bindingContext, exchange));});
}

ResponseEntityResultHandler(响应Entity)

WebHandlerDecorator

通过代理执行对应的方法

public Mono<Void> handle(ServerWebExchange exchange) {return this.delegate.handle(exchange);
}

HttpWebHandlerAdapter

FilteringWebHandler(过滤WebHandler)

完成过滤链的创建

public FilteringWebHandler(WebHandler handler, List<WebFilter> filters) {super(handler);this.chain = new DefaultWebFilterChain(handler, filters);
}

ExceptionHandlingWebHandler(异常HandlingWebHandler)

初始化

public ExceptionHandlingWebHandler(WebHandler delegate, List<WebExceptionHandler> handlers) {super(delegate);List<WebExceptionHandler> handlersToUse = new ArrayList();handlersToUse.add(new CheckpointInsertingHandler());handlersToUse.addAll(handlers);this.exceptionHandlers = Collections.unmodifiableList(handlersToUse);
}

执行
1.先执行正常的WebHandler
2.执行完成之后,如果异常则执行所有异常类。

public Mono<Void> handle(ServerWebExchange exchange) {Mono<Void> completion;try {completion = super.handle(exchange);}catch (Throwable ex) {completion = Mono.error(ex);}for (WebExceptionHandler handler : this.exceptionHandlers) {completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));}return completion;
}

RouterFunctionWebHandler

public Mono<Void> handle(ServerWebExchange exchange) {return Mono.defer(() -> {ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders());addAttributes(exchange, request);return this.routerFunction.route(request).defaultIfEmpty(notFound()).flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request))).flatMap(response -> wrapException(() -> response.writeTo(exchange,new HandlerStrategiesResponseContext(this.strategies))));});
}private void addAttributes(ServerWebExchange exchange, ServerRequest request) {Map<String, Object> attributes = exchange.getAttributes();attributes.put(REQUEST_ATTRIBUTE, request);
}

WebFilter

Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain);

Spring Boot AutoConfigurer自动配置

根据spring.factories有以下自动启类

HttpHandlerAutoConfiguration(HttpHandler自动配置)

HttpHandler完成了WebFilterChain的装配,使所有HTTP请求都经过WebFilterChain过滤执行。

@Configuration(proxyBeanMethods = false)
public static class AnnotationConfig {@Beanpublic HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();WebFluxProperties properties = propsProvider.getIfAvailable();if (properties != null && StringUtils.hasText(properties.getBasePath())) {Map<String, HttpHandler> handlersMap = Collections.singletonMap(properties.getBasePath(), httpHandler);return new ContextPathCompositeHandler(handlersMap);}return httpHandler;}}

WebHttpHandlerBuilder

创建WebHttpHandlerBuilder
1.通过上下文Bean获取名称为"webHandler"且类为WebHandler
2.通过上下文获取所有的WebFilter并进行排序。
3.对所有WebFilter类进行处理和过滤
4.通过上下文获取所有的WebExceptionHandler类并进行排序。
5.对所有WebExceptionHandler类进行处理和过滤
6.通过上下文Bean获取名称为"webSessionManager"且类为WebSessionManager
7.通过上下文Bean获取名称为"serverCodecConfigurer"且类为ServerCodecConfigurer
8.通过上下文Bean获取名称为"localeContextResolver"且类为LocaleContextResolver
9.通过上下文Bean获取名称为"forwardedHeaderTransformer"且类为ForwardedHeaderTransformer

public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {WebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class), context);List<WebFilter> webFilters = context.getBeanProvider(WebFilter.class).orderedStream().collect(Collectors.toList());builder.filters(filters -> filters.addAll(webFilters));List<WebExceptionHandler> exceptionHandlers = context.getBeanProvider(WebExceptionHandler.class).orderedStream().collect(Collectors.toList());builder.exceptionHandlers(handlers -> handlers.addAll(exceptionHandlers));try {builder.sessionManager(context.getBean(WEB_SESSION_MANAGER_BEAN_NAME, WebSessionManager.class));}catch (NoSuchBeanDefinitionException ex) {// Fall back on default}try {builder.codecConfigurer(context.getBean(SERVER_CODEC_CONFIGURER_BEAN_NAME, ServerCodecConfigurer.class));}catch (NoSuchBeanDefinitionException ex) {// Fall back on default}try {builder.localeContextResolver(context.getBean(LOCALE_CONTEXT_RESOLVER_BEAN_NAME, LocaleContextResolver.class));}catch (NoSuchBeanDefinitionException ex) {// Fall back on default}try {builder.forwardedHeaderTransformer(context.getBean(FORWARDED_HEADER_TRANSFORMER_BEAN_NAME, ForwardedHeaderTransformer.class));}catch (NoSuchBeanDefinitionException ex) {// Fall back on default}return builder;
}

1.HttpHandler的构造器
2.创建WebFilterChain过滤器通过FilteringWebHandler创建DefaultWebFilterChain,而DefaultWebFilterChain实现了WebFilterChain。
3.创建ExceptionHandlingWebHandler并将FilteringWebHandler设置进去。
4.创建HttpWebHandlerAdapter,并将WebHandler设置到adapted中。
5.

public HttpHandler build() {WebHandler decorated = new FilteringWebHandler(this.webHandler, this.filters);WebHandler decorated = new ExceptionHandlingWebHandler(decorated, this.exceptionHandlers);HttpWebHandlerAdapter adapted = new HttpWebHandlerAdapter(decorated);if (this.sessionManager != null) {adapted.setSessionManager(this.sessionManager);}if (this.codecConfigurer != null) {adapted.setCodecConfigurer(this.codecConfigurer);}if (this.localeContextResolver != null) {adapted.setLocaleContextResolver(this.localeContextResolver);}if (this.forwardedHeaderTransformer != null) {adapted.setForwardedHeaderTransformer(this.forwardedHeaderTransformer);}if (this.applicationContext != null) {adapted.setApplicationContext(this.applicationContext);}adapted.afterPropertiesSet();return adapted;
}

FilteringWebHandler

public FilteringWebHandler(WebHandler handler, List<WebFilter> filters) {super(handler);this.chain = new DefaultWebFilterChain(handler, filters);
}
DefaultWebFilterChain

实现了WebFilterChain
初始化
1.创建DefaultWebFilterChain
2.通过initChain来创建过滤链,如果WebFliter存在多个。则创建WebFliter的链。

public DefaultWebFilterChain(WebHandler handler, List<WebFilter> filters) {Assert.notNull(handler, "WebHandler is required");this.allFilters = Collections.unmodifiableList(filters);this.handler = handler;DefaultWebFilterChain chain = initChain(filters, handler);this.currentFilter = chain.currentFilter;this.chain = chain.chain;
}private static DefaultWebFilterChain initChain(List<WebFilter> filters, WebHandler handler) {DefaultWebFilterChain chain = new DefaultWebFilterChain(filters, handler, (WebFilter)null, (DefaultWebFilterChain)null);for(ListIterator<? extends WebFilter> iterator = filters.listIterator(filters.size()); iterator.hasPrevious(); chain = new DefaultWebFilterChain(filters, handler, (WebFilter)iterator.previous(), chain)) {}return chain;
}

过滤链执行
1.通过Mono.defer()完成订阅功能,当有事件发布(如:前端发起请求)的时候就可以收到。
2.判断当前currentFilter是否为空,不为空则执行WebFilter的过滤链,
3.当WebFilter执行完之后,执行WebHandler。

public Mono<Void> filter(ServerWebExchange exchange) {return Mono.defer(() -> {return this.currentFilter != null && this.chain != null ? this.invokeFilter(this.currentFilter, this.chain, exchange) : this.handler.handle(exchange);});
}private Mono<Void> invokeFilter(WebFilter current, DefaultWebFilterChain chain, ServerWebExchange exchange) {String currentName = current.getClass().getName();return current.filter(exchange, chain).checkpoint(currentName + " [DefaultWebFilterChain]");
}

ReactiveWebServerFactoryAutoConfiguration(ReactiveWeb服务器工厂自动配置)

默认支持4中方式自启动服务器:Tomcat、Jetty、Undertow、Netty

@Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class })

Spring-boot-starter-webflux默认启动是nettty

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-reactor-netty</artifactId><version>2.3.12.RELEASE</version><scope>compile</scope>
</dependency>

WebFluxAutoConfiguration(WebFlux自动配置)

OrderedHiddenHttpMethodFilter

继承自HiddenHttpMethodFilter并实现OrderedWebFilter,而HiddenHttpMethodFilter
实现了WebFilter

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.webflux.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();
}

WebFluxConfig(WebFlux配置)

1.引入EnableWebFluxConfiguration类,在初始化当前类之前完成该类初始化。
2.启动配置参数ResourceProperties类和WebFluxProperties类
3.通过addResourceHandlers(添加资源Handlers)完成对"/webjars/**"目录和"classpath:/META-INF/resources/webjars/"目录资源的加载。 还可以通过WebFluxProperties配置添加自定义资源目录和本地目录。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ ResourceProperties.class, WebFluxProperties.class })
@Import({ EnableWebFluxConfiguration.class })
public static class WebFluxConfig implements WebFluxConfigurer { @Overridepublic 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);}}
}

EnableWebFluxConfiguration(启动WebFlux配置)

继承自DelegatingWebFluxConfiguration,而DelegatingWebFluxConfiguration继承自WebFluxConfigurationSupport
1.注解@Configuration实现自动转为Bean

@Configuration(proxyBeanMethods = false)
public static class EnableWebFluxConfiguration extends DelegatingWebFluxConfiguration {private final WebFluxProperties webFluxProperties;private final WebFluxRegistrations webFluxRegistrations;public EnableWebFluxConfiguration(WebFluxProperties webFluxProperties,ObjectProvider<WebFluxRegistrations> webFluxRegistrations) {this.webFluxProperties = webFluxProperties;this.webFluxRegistrations = webFluxRegistrations.getIfUnique();}@Bean@Overridepublic FormattingConversionService webFluxConversionService() {Format format = this.webFluxProperties.getFormat();WebConversionService conversionService = new WebConversionService(new DateTimeFormatters().dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));addFormatters(conversionService);return conversionService;}@Bean@Overridepublic Validator webFluxValidator() {if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {return super.webFluxValidator();}return ValidatorAdapter.get(getApplicationContext(), getValidator());}@Overrideprotected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {if (this.webFluxRegistrations != null) {RequestMappingHandlerAdapter adapter = this.webFluxRegistrations.getRequestMappingHandlerAdapter();if (adapter != null) {return adapter;}}return super.createRequestMappingHandlerAdapter();}@Overrideprotected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {if (this.webFluxRegistrations != null) {RequestMappingHandlerMapping mapping = this.webFluxRegistrations.getRequestMappingHandlerMapping();if (mapping != null) {return mapping;}}return super.createRequestMappingHandlerMapping();}}

ErrorWebFluxAutoConfiguration(异常WebFlux自动配置)

当异常时候的处理

ErrorWebExceptionHandler

@Bean
@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
@Order(-1)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,ResourceProperties resourceProperties, ObjectProvider<ViewResolver> viewResolvers,ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,resourceProperties, this.serverProperties.getError(), applicationContext);exceptionHandler.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()));exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());return exceptionHandler;
}

DefaultErrorAttributes

@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();
}

ClientHttpConnectorAutoConfiguration(客户端Http连接自动配置)

WebClientCustomizer

@Bean
@Lazy
@Order(0)
@ConditionalOnBean(ClientHttpConnector.class)
public WebClientCustomizer clientConnectorCustomizer(ClientHttpConnector clientHttpConnector) {return (builder) -> builder.clientConnector(clientHttpConnector);
}

ClientHttpConnectorConfiguration

@Import({ ClientHttpConnectorConfiguration.ReactorNetty.class, ClientHttpConnectorConfiguration.JettyClient.class })ReactorNetty
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(reactor.netty.http.client.HttpClient.class)
@ConditionalOnMissingBean(ClientHttpConnector.class)
public static class ReactorNetty {@Bean@ConditionalOnMissingBeanpublic ReactorResourceFactory reactorClientResourceFactory() {return new ReactorResourceFactory();}@Bean@Lazypublic ReactorClientHttpConnector reactorClientHttpConnector(ReactorResourceFactory reactorResourceFactory,ObjectProvider<ReactorNettyHttpClientMapper> mapperProvider) {ReactorNettyHttpClientMapper mapper = mapperProvider.orderedStream().reduce((before, after) -> (client) -> after.configure(before.configure(client))).orElse((client) -> client);return new ReactorClientHttpConnector(reactorResourceFactory, mapper::configure);}}

WebClientAutoConfiguration(Web客户端自动配置)

@AutoConfigureAfter({ CodecsAutoConfiguration.class, 
ClientHttpConnectorAutoConfiguration.class })

WebClient

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public WebClient.Builder webClientBuilder(ObjectProvider<WebClientCustomizer> customizerProvider) {WebClient.Builder builder = WebClient.builder();customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));return builder;
}

WebClientCodecCustomizer

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(CodecCustomizer.class)
protected static class WebClientCodecsConfiguration {@Bean@ConditionalOnMissingBean@Order(0)public WebClientCodecCustomizer exchangeStrategiesCustomizer(ObjectProvider<CodecCustomizer> codecCustomizers) {return new WebClientCodecCustomizer(codecCustomizers.orderedStream().collect(Collectors.toList()));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18823.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM day9 (按键中断控制led亮灭)

key.h #ifndef __KEY_H__ #define __KEY_H__#include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_uart.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_gic.h"//事件号 #define EXTI_…

学无止境·MySQL④(多表查询)

多表查询试题 试题一1、创建表2、表中添加数据3、查询每个部门的所属员工4、查询研发部门的所属员工5、查询研发部和销售部的所属员工6、查询每个部门的员工数,并升序排序7、查询人数大于等于3的部门&#xff0c;并按照人数降序排序 试题一 1、创建表 use mydb3; – 创建部门…

NAT介绍

目录 NAT NAT的配置——配置位置都是在边界路由器的出接口上进行配置 静态NAT 动态NAT——多对多的NAT NAPT——easy IP 多对多的NAPT 端口映射——高级用法 NAT——网络地址转换 IPV4地址不够用 NAT ABC——三类地址中截取了一部分地址&#xff08;并且让这一部分地址可以重复…

MySQL-分库分表详解(一)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

【数据结构】24王道考研笔记——串

四、串 串的定义 串&#xff08;字符串&#xff09;是由零个或多个字符组成的有限序列。 子串&#xff1a;串中任意个连续的字符组成的子序列主串&#xff1a;包含子串的串字符在主串中的位置&#xff1a;字符在串中的序号子串在主串中的位置&#xff1a;子串的第一个字符在…

docker 里面各种 command not found 总结

一、ip&#xff1a;command not found 执行命令&#xff1a; apt-get update & apt-get install -y iproute2 二、yum&#xff1a;command not found 执行命令&#xff1a; apt-get update & apt-get install -y yum 三、ping&#xff1a;command not found 执行命…

一、简易搭建本地CAS服务端

CAS服务端war包下载 https://repo1.maven.org/maven2/org/apereo/cas/cas-server-webapp-tomcat/5.3.14/ 可使用迅雷下载cas-server-webapp-tomcat-5.3.14.war &#xff0c;速度很快 将wab包放到本地tomcat的webapps下D:\tomcat\apache-tomcat-8.5.63\webapps\cas\WEB-INF\clas…

解决Anaconda第三方库下载慢

1.打开Anconda Prompt&#xff0c;进入后台 2.执行命令第一个是添加一个清华镜像&#xff0c;第二个设置在 conda 显示通道的 URL。 &#xff08;1&#xff09;conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ &#xff08;2&#xff0…

RocketMQ5.0消息存储<四>_刷盘机制

RocketMQ5.0消息存储<四>_刷盘机制 一、刷盘概览 RocketMQ存储与读写是基于JDK NIO的内存映射机制(MappedByteBuffer),消息存储时首先将消息追加到文件内存映射(commit操作),再根据配置的刷盘策略在不同时间进行刷写到磁盘(flush操作)。同步刷盘,消息提交到文件内…

【每天40分钟,我们一起用50天刷完 (剑指Offer)】第二十二天 22/50

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

C#核心知识回顾——12.lambda表达式、List排序、协变和逆变

1.Lambda表达式 可以将lambad表达式理解为匿名函数的简写 它除了写法不同外&#xff0c;使用上和匿名函数一模一样 都是和委托或者事件配合使用的 //匿名函数 //delegate&#xff08;参数列表&#xff09; //{ //} //lambda表达式 //(参数列表) > //{ //函数体 //…

【Maven】类或者包提示找不到,报红

背景 使用IDEA&#xff0c;类或者包提示找不到&#xff0c;报红 解决方法 1. maven reload 2. 检查profiles是否对 3. 不要选中offline模式 4. 检查本地仓库位置 5. 清掉idea缓存 6. 到本地maven仓库删掉出错的包然后重新maven reload 7. update本地仓库 8. 排查是不是别人没…