SpringCloud Gateway DispatcherHandler调用方法详细介绍

目录
  • 前言
  • DispatcherHandler类声明
  • handle方法
  • 最后总结一下

前言

之前几节我们分析了请求是如何调用到HttpWebHandlerAdapter的,然后再调用到DispatcherHandler中,通过handle方法来处理具体的请求。

DispatcherHandler的注入在自动装配那一节已经说过了,忘记的同学可以重新会看一下。

DispatcherHandler类声明

public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware {

可以看到实现了ApplicationContextAware接口,并实现了setApplicationContext方法

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		initStrategies(applicationContext);
	}
	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);
	}

可以看到,这里初始化了核心的三部分内容,具体是handlerMappingshandlerAdaptersresultHandlers

通过名字我们可以猜想它们的具体作用,后面会详细解析

  • 通过handlerMappings确定具体处理网关请求的handler
  • 通过handlerAdapters确定具体支持处理handler的handlerAdapter,并处理handler。因为负责处理请求的方式不只一种,可以是注解的方法,也可以是函数式接口(lambda 表达式),也就是通过handlerMappings映射返回的对象是不一样的, 要把它们适配成统一的API。
  • 最终由resultHandler生成返回结果

handle方法

接下来我们来看下最核心的handle方法是如何具体处理请求的

	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		if (this.handlerMappings == null) {
			return createNotFoundError();
		}
		if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
			return handlePreFlight(exchange);
		}
         //循环遍历所有的handlerMappings通过getHandler找到具体的handler
		return Flux.fromIterable(this.handlerMappings)
				.concatMap(mapping -> mapping.getHandler(exchange))
                  //next方法表示如果循环遍历handlerMappings时有多个匹配的handler,通过next方法只取出第一个匹配的handler执行下面的调用方法
				.next()
				.switchIfEmpty(createNotFoundError())
                   //处理具体的handler
				.flatMap(handler -> invokeHandler(exchange, handler))
                   //处理结果
				.flatMap(result -> handleResult(exchange, result));
	}

首先看getHandler方法

   	AbstractHandlerMapping.java
    @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 (config != null) {
					config.validateAllowCredentials();
				}
				if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
					return NO_OP_HANDLER;
				}
			}
			return handler;
		});
	}

其中会调用getHandlerInternal方法,是一个抽象方法

protected abstract Mono<?> getHandlerInternal(ServerWebExchange exchange);

通过断点调试可以看到,其中handlerMappings一共有四个

getHandlerInternal方法具体实现

可以分别去每个实现方法中断点查看不难找到RoutePredicateHandlerMapping就是我们需要的,通过名字可以猜到就是用来处理路由转发的

找到它的getHandlerInternal实现方法

	@Override
	protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
		// don't handle requests on management port if set and different than server port
		if (this.managementPortType == DIFFERENT && this.managementPort != null
				&& exchange.getRequest().getURI().getPort() == this.managementPort) {
			return Mono.empty();
		}
		exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
        //lookupRoute方法将根据配置文件中指定的predicates断言器和请求路径做匹配,如果匹配成功就会返回FilteringWebHandler
		return lookupRoute(exchange)
				// .log("route-predicate-handler-mapping", Level.FINER) //name this
				.flatMap((Function<Route, Mono<?>>) r -> {
					exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
					if (logger.isDebugEnabled()) {
						logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
					}
					exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
                      //上面定义的,private final FilteringWebHandler webHandler;
					return Mono.just(webHandler);
				}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
					exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
					if (logger.isTraceEnabled()) {
						logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
					}
				})));
	}

lookupRoute关于路由相关的以后单独再讲,现在知道了返回的handler就是FilteringWebHandler

下面关注invokeHandler方法

	private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
		if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) {
			return Mono.empty();  // CORS rejection
		}
         //遍历所有的handlerAdapters找到能够支持当前handler的handlerAdapter,并执行handle方法
		if (this.handlerAdapters != null) {
			for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
				if (handlerAdapter.supports(handler)) {
					return handlerAdapter.handle(exchange, handler);
				}
			}
		}
		return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
	}

可以看到supports方法具体有四个实现

	@Override
	public boolean supports(Object handler) {
		return handler instanceof HandlerFunction;
	}
	@Override
	public boolean supports(Object handler) {
		return handler instanceof HandlerMethod;
	}
	@Override
	public boolean supports(Object handler) {
		return WebHandler.class.isAssignableFrom(handler.getClass());
	}
	@Override
	public boolean supports(Object handler) {
		return WebSocketHandler.class.isAssignableFrom(handler.getClass());
	}

我们目前支持的就是WebHandler,也就是会执行SimpleHandlerAdapter中的handle方法

	@Override
	public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
         //FilteringWebHandler
		WebHandler webHandler = (WebHandler) handler;
		Mono<Void> mono = webHandler.handle(exchange);
		return mono.then(Mono.empty());
	}

之后会执行FilteringWebHandler的handle方法

	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
         //找到该请求对应的路由
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
         //找到该路由对应的GatewayFilter
		List<GatewayFilter> gatewayFilters = route.getFilters();
		//得到全局的的GatewayFilter
		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
		combined.addAll(gatewayFilters);
		// TODO: needed or cached?
         //对全局过滤器和配置文件指定的路由局部过滤器统一排序
		AnnotationAwareOrderComparator.sort(combined);

		if (logger.isDebugEnabled()) {
			logger.debug("Sorted gatewayFilterFactories: " + combined);
		}
		//生成Gateway过滤器链,然后对客户端请求进行处理
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}

首先先看一下FilteringWebHandler在哪被注入的,通过搜索发现在GatewayAutoConfiguration中注入的

	@Bean
	public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
		return new FilteringWebHandler(globalFilters);
	}

通过调试可以看到注入所有的GlobalFilter,默认有10个,并初始化

	public FilteringWebHandler(List<GlobalFilter> globalFilters) {
        //初始化时加载所有全局过滤器,将他们适配为GatewayFilter类型,方便等会做合并
		this.globalFilters = loadFilters(globalFilters);
	}
    //适配器模式,通过嵌套把GlobalFilter封装成GatewayFilter类型
	private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
		return filters.stream().map(filter -> {
             //GatewayFilterAdapter持有GlobalFilter对象
			GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
			if (filter instanceof Ordered) {
                  //将后续用来排序的order值传进来
				int order = ((Ordered) filter).getOrder();
                  //OrderedGatewayFilter同样持有GatewayFilter对象
				return new OrderedGatewayFilter(gatewayFilter, order);
			}
			return gatewayFilter;
		}).collect(Collectors.toList());
	}

至于这里为什么要返回两种不同的对象分别持有GatewayFilter,主要和后面的排序相关

对于实现了Ordered接口的filter会返回OrderedGatewayFilter对象

接着回到前面的handle方法中,其中有一步就是用来排序的

AnnotationAwareOrderComparator.sort(combined);

通过调试可以找到具体的实现在OrderComparator

	@Override
	public int compare(@Nullable Object o1, @Nullable Object o2) {
		return doCompare(o1, o2, null);
	}
	private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
        //谁实现了PriorityOrdered接口就代表谁的优先级高
		boolean p1 = (o1 instanceof PriorityOrdered);
		boolean p2 = (o2 instanceof PriorityOrdered);
		if (p1 && !p2) {
			return -1;
		}
		else if (p2 && !p1) {
			return 1;
		}
		int i1 = getOrder(o1, sourceProvider);
		int i2 = getOrder(o2, sourceProvider);
		return Integer.compare(i1, i2);
	}

如果都实现了PriorityOrdered接口或者都没有实现,那么通过getOrder方法得到的值来排序

	private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
		Integer order = null;
		if (obj != null && sourceProvider != null) {
			Object orderSource = sourceProvider.getOrderSource(obj);
			if (orderSource != null) {
				if (orderSource.getClass().isArray()) {
					for (Object source : ObjectUtils.toObjectArray(orderSource)) {
						order = findOrder(source);
						if (order != null) {
							break;
						}
					}
				}
				else {
					order = findOrder(orderSource);
				}
			}
		}
		return (order != null ? order : getOrder(obj));
	}

传入的sourceProvider为null,最终调用另外一个getOrder方法

	protected int getOrder(@Nullable Object obj) {
		if (obj != null) {
			Integer order = findOrder(obj);
			if (order != null) {
				return order;
			}
		}
		return Ordered.LOWEST_PRECEDENCE;
	}

进入findOrder方法

	@Nullable
	protected Integer findOrder(Object obj) {
		return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
	}

可以看到,如果实现了Ordered接口,则调用重写的getOrder方法,否则返回null,最后会返回

Ordered.LOWEST_PRECEDENCE,代表最低的优先级

int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

后面通过Integer.compare方法来比较,值越大的优先级越低

最后总结一下

  • 谁实现了PriorityOrdered接口就代表谁的优先级高
  • 如果都没有实现或者都实现了,那么判断是否实现了Ordered接口
  • 如果实现了Ordered接口,那么返回实现方法中getOrder的值
  • 如果没有实现Ordered接口,那么返回最低的优先级也就是Integer的最大值
  • 最后在比较两个值的大小,值越大代表优先级越小,值越小代表优先级越大

到此这篇关于SpringCloud Gateway DispatcherHandler调用方法详细介绍的文章就介绍到这了,更多相关SpringCloud Gateway DispatcherHandler内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringCloud Gateway中GatewayFilterChain执行流程详解

    上一节我们把FilteringWebHandler中handle方法的过滤器统一排序的那部分逻辑讲完了 接着就是生成过滤器器链,执行过滤方法 return new DefaultGatewayFilterChain(combined).filter(exchange); @Override public Mono<Void> filter(ServerWebExchange exchange) { return Mono.defer(() -> { if (this.index <

  • SpringCloud服务网关Gateway的使用教程详解

    目录 Gateway 什么是Gateway 什么是api网关 网关的三个核心概念 路由(Route) 断言(Predicate) 过滤(Filter) gateway的工作流程 如何使用Gateway gateway路由转发 使用配置文件 使用代码配置 路由实现负载均衡 gateway九种断言 gateway过滤修改 Gateway 什么是Gateway   由于Netflix的zuul发生问题,spring公司自己研发了一套网关框架Gateway用于取代zuul的使用.什么是gateway呢?

  • SpringCloud Gateway自动装配实现流程详解

    目录 启动依赖 WebFluxAutoConfiguration HttpHandlerAutoConfiguration 总结一下 启动依赖 找到gateway的依赖,spring-cloud-starter-gateway <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId>

  • SpringCloud Gateway HttpWebHandlerAdapter链路调用请求流程介绍

    目录 前言 web容器 前言 上一节我们说到从HttpWebHandlerAdapter的handle方法说起到DispatcherHandler的调用流程 那么HttpWebHandlerAdapter的handle方法是从哪里来调用的呢? 我们可以找下看哪些类使用了HttpHandler 通过这些类的名字,我们可以发现,HttpHandler 往下就是具体的 Web 容器了,也就是说有具体的 Web 容器来接受请求,然后通过调用 HttpWebHandlerAdapter#handler 来

  • SpringCloud中Gateway的使用教程详解

    目录 1.基础教程 2.将配置放在配置文件里 3.放在springcloud里面 4.使用服务名而不是IP 1.基础教程 pom.xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.6</version> </parent>

  • SpringCloud Gateway DispatcherHandler调用方法详细介绍

    目录 前言 DispatcherHandler类声明 handle方法 最后总结一下 前言 之前几节我们分析了请求是如何调用到HttpWebHandlerAdapter的,然后再调用到DispatcherHandler中,通过handle方法来处理具体的请求. DispatcherHandler的注入在自动装配那一节已经说过了,忘记的同学可以重新会看一下. DispatcherHandler类声明 public class DispatcherHandler implements WebHand

  • SpringCloud使用Feign实现远程调用流程详细介绍

    目录 前言 1. 导入依赖坐标 2. 开启Feign自动装配 3. 声明远程调用 4. 替代RestTemplate 5. 测试 前言 本次示例代码的文件结构如下图所示. 1. 导入依赖坐标 在 order-service 的 pom.xml 文件中导入 Feign 的依赖坐标. <!-- Feign远程调用客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifa

  • Spring事务传播中嵌套调用实现方法详细介绍

    目录 前言 7种传播方式 注解式事务 事务的方法之间的调用 注意事项 前言 最近在使用Spring框架时遇到了一些问题,主要是Spring的事务传播问题,一个不带事务的方法调用带事务的方法,有时候会出现不回滚的情况,所以写了这篇文章来记录一下. 7种传播方式 我们先来看Spring事务的7中传播方式以及对应的描述 属性名称 值 描述 PROPAGATION__REQUIRED REQUIRED 表示的是当前这个方法必须运行在一个事务环境中,如果当前方法已经处于事务环境中,就可以直接使用该方法,否

  • 重写hashCode()和equals()方法详细介绍

    hashCode()和equals()方法可以说是Java完全面向对象的一大特色.它为我们的编程提供便利的同时也带来了很多危险.这篇文章我们就讨论一下如何正解理解和使用这2个方法. 如何重写equals()方法 如果你决定要重写equals()方法,那么你一定要明确这么做所带来的风险,并确保自己能写出一个健壮的equals()方法.一定要注意的一点是,在重写equals()后,一定要重写hashCode()方法.具体原因稍候再进行说明. 我们先看看 JavaSE 7 Specification中

  • java中Object类4种方法详细介绍

    目录 Object(四大方法): hashCode()方法: equals()方法: getClass()方法: toString()方法: 总结 Object(四大方法): 文章干货满满,耐性看完~~何为Object?首先先来看看官方对Object的介绍:在这里附上Java官方的查阅工具:https://docs.oracle.com/en/java/javase/17/docs/api/index.html 由官方介绍可见,object属于Java.lang包内的一个类,而且提供了很多种方法

  • Android webview加载H5方法详细介绍

    目录 1,安卓APP 怎么用webview加载H5 2,H5怎么调用安卓定义的方法 3,安卓怎么调用H5定义的方法 这篇文章主要阐述3个知识点 安卓APP 怎么用webview加载H5 H5怎么调用安卓定义的方法 安卓怎么调用H5定义的方法 1,安卓APP 怎么用webview加载H5 安卓端定义个webview xml 页面,代码如下所示: <?xml version="1.0" encoding="utf-8"?> <WebView xmlns

  • Vue路由配置方法详细介绍

    目录 手动配置Vue-router环境 组件内部跳转路由与传参useRouter,useRoute 手动配置Vue-router环境 1.下载包: npm i vue-router --save或者 npm i vue-router --S 或者用cdn引入 2.创建路由的js文件(路由.子路由.重定向.开启history模式) createRouter.createWebHistory //路由文件 import { createRouter, createWebHistory } from

  • React网络请求发起方法详细介绍

    目录 1. 发起网络请求 2. 开发时网络请求代理配置 1. 发起网络请求 首先需要安装 axios 库: yarn add axios 发起网络请求: import React, { Component } from 'react' import { get } from './utils/http' import Loading from './components/Loading' class App extends Component { state = { users: null }

  • Android使用元数据实现配置信息的传递方法详细介绍

    目录 前序 一.在代码中获取元数据 二.给应用页面注册快捷方式 前序 格式 <meta-data android:name="weather" android:value="xxx"/> 什么场景需要使用? 使用第三方SDK,需要在APP应用内使用别的APP的整合包,如使用微信登录.某某地图等. 一.在代码中获取元数据 在java代码中,获取元数据信息的步骤分为下列三步: 调用getPackageManager方法获得当前应用的包管理器: 调用包管理器的

  • Java Feign微服务接口调用方法详细讲解

    目录 Feign说明 引入依赖启动类开启客户端 Feign接口开发 编写容错类 在业务层调用Feign客户端接口 Feign的常用属性如下 Feign说明 Feign是一种声明式.模板化的HTTP客户端.在spring cloud中使用Feign,可以做到类似于普通的接口的请求调用,可以发现对应的服务的接口,进而直接调用对应服务中的接口. 引入依赖启动类开启客户端 首先需要引入依赖 <dependency> <groupId>org.springframework.cloud<

随机推荐