Reactive Feign源码深度剖析:基于动态代理的Feign接口封装处理全解析

文章目录

前言

该篇博文,主要通过源码分析方式,介绍Reactive Feign接口,底层封装过程,并借此解答上一篇博文一文读懂Reactive Feign:底层请求调用原理及实现细节提及的部分源码类的串联过程。

通过该篇博文,大致可以了解如下内容:

  1. Feign接口,在Spring环境中,被代理成哪个Bean,进行实际使用。
  2. Feign接口代理类,内部封装逻辑。
  3. Feign请求,数据流转方式。

一、Feign接口,代理类创建过程

相信大家知道,在配置类上,通过EnableFeignClients注解,启用Reactive Feign。该注解,通过Import注解,引入ReactiveFeignClientsRegistrar。通过该类,扫描ReactiveFeignClient注解标记的接口,并通过动态代理,装配成Bean。

关于这部分内容,与FeignClient代理类创建过程分析类似,此处不再赘述。不同点在于,Reactive Feign通过ReactiveFeignClientFactoryBean,开启代理类功能。后续内容,也是基于该类,进行分析。

二、Feign代理类,请求处理逻辑封装

ReactiveFeignClientFactoryBean,实现FactoryBean接口,通过getObject为获取Bean实例。此处,将通过geiObject方法,开启这部分内容的源码分析。

java 复制代码
// ReactiveFeignClientFactoryBean.java 内容
@Override
public Object getObject() {
	return getTarget();
}

/**
 * @param <T> the target type of the Feign client
 * @return a {@link ReactiveFeign} client created with the specified data and the context information
 */
private <T> T getTarget() {
	ReactiveFeignNamedContext namedContext = new ReactiveFeignNamedContext(applicationContext, name);
	ReactiveFeignBuilder builder = namedContext.get(ReactiveFeignBuilder.class)
			.contract(namedContext.get(Contract.class));

	builder = applyConfigurators(builder, namedContext);

	if (decode404) {
		builder = builder.decode404();
	}

	builder = fallback(builder, namedContext);

	// 通过判断url是否有值,确定是否需要开启负载均衡。this.name,值为服务实例名称。
	// builder,对应于ReactiveFeignBuilder
	return StringUtils.hasText(this.url)
			? (T) builder.target(type, buildUrl())
			: (T) builder.target(type, this.name, buildUrl());
}

// ReactiveFeignBuilder.java 内容
default T target(final Target<T> target) {
    return build().newInstance(target);
}

// ReactiveFeign构造方法,三个参数类型,分别为ReactiveContract、ReactiveMethodHandlerFactory、ReactiveInvocationHandler.Factory
// ReactiveContract,内部引入SpringMvcContract,并在SpringMvcContract,完成信息收集工作,在进一步判断返回值类型,是否满足Reactor要求。
default ReactiveFeign build() {
    return new ReactiveFeign(contract(),
            buildReactiveMethodHandlerFactory(buildReactiveClientFactory()),
            invocationHandlerFactory());
}

接下来,我们将开始分析ReactiveFeign.newInstance方法,该方法,是一个比较重要的方法,在该方法内部,结合SpringMvcContract、ReactiveMethodHandlerFactory、ReactiveInvocationHandler.Factory,创建MethodHandler实例,后续,请求是通过Handler完成的处理。在这里,我们首先介绍MethodHandler的创建过程

java 复制代码
Map<String, MethodHandler> targetToHandlersByName(final Target target) {
    // 此处使用SpringMvcContract,收集接口方法信息。MethodMetadata,包含接口类、方法、方法签名等信息。
	Map<String, MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type())
	        .stream()
	        .collect(Collectors.toMap(
	                MethodMetadata::configKey,
	                md -> md
	        ));
	Map<String, Method> configKeyToMethod = Stream.of(target.type().getMethods())
	        .collect(Collectors.toMap(
	                method -> Feign.configKey(target.type(), method),
	                method -> method
	        ));
	
	final Map<String, MethodHandler> result = new LinkedHashMap<>();
	
	methodHandlerFactory.target(target);
	
	for (final Map.Entry<String, Method> entry : configKeyToMethod.entrySet()) {
		String configKey = entry.getKey();
		MethodMetadata md = metadata.get(configKey);
		MethodHandler methodHandler = md != null
					// 通过ReactiveMethodHandlerFactory.create方法,创建MethodHandler实例。
		        ? methodHandlerFactory.create(md)
		        : methodHandlerFactory.createDefault(entry.getValue());  //isDefault(entry.getValue())
		result.put(configKey, methodHandler);
	}
	
	return result;
}

// ReactiveMethodHandlerFactory.java 内容
@Override
public MethodHandler create(MethodMetadata metadata) {
	// 首先创建PublisherClientMethodHandler实例,其内部包含PublisherHttpClient实例,该用例,完成请求执行动作。
	MethodHandler methodHandler = new PublisherClientMethodHandler(
			target, metadata, publisherClientFactory.create(metadata));

	if(isResponsePublisher(metadata.returnType())){
		return new MonoMethodHandler(methodHandler);
	}

    // 通过返回值,分别构造MonoMethodHandler、FluxMethodHandler,其内部,都是通过PublisherClientMethodHandler,完成请求执行动作。这两个MethodHandler,仅仅将结果通过Mono.from、Flux.from,封装PublisherClientMethodHandler执行处理动作。
	Type returnPublisherType = returnPublisherType(metadata);
	if(returnPublisherType == Mono.class){
		return new MonoMethodHandler(methodHandler);
	} else if(returnPublisherType == Flux.class) {
		return new FluxMethodHandler(methodHandler);
	} else {
		throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
	}
}

// PublisherClientMethodHandler内容
@Override
public Publisher<?> invoke(final Object[] argv) {
	// 其中publisherClient,为LoadBalancerPublisherClient类型实例。
    return publisherClient.executeRequest(buildRequest(argv));
}

// LoadBalancerPublisherClient.java内容。
@Override
public Publisher<Object> executeRequest(ReactiveHttpRequest request) {
    return Mono.from(reactiveLoadBalancer.choose())
            .flatMapMany(serviceInstanceResponse -> {
                URI lbUrl = reconstructURI(serviceInstanceResponse.getServer(), request.uri());
                ReactiveHttpRequest lbRequest = new ReactiveHttpRequest(request, lbUrl);
                // publisherClient,为MonoPublisherHttpClient
                return publisherClient.executeRequest(lbRequest);
            });
}

到此,就基本上结束Reactive Feign,请求内部处理逻辑的封装。再次,做一个简单的总结

Reactive Feign接口,通过动态代理,创建代理类,并注册Spring Bean。当接口调用时,内部处理逻辑,最终通过LoadBalancerPublisherClient,进行请求处理,然后,根据返回值类型,将其封装成Mono、Flux。

三、Feign请求,数据流转

在这里,主要介绍MonoPublisherHttpClient,用于揭开Reactive Feign,内部,如何执行请求。

java 复制代码
// MonoPublisherHttpClient.java 内容
@Override
public Mono<Object> executeRequest(ReactiveHttpRequest request) {
	// reactiveHttpClient为WebReactiveHttpClient实例
	Mono<ReactiveHttpResponse> response = Mono.defer(() -> reactiveHttpClient.executeRequest(request));
	return response.flatMap(resp -> Mono.from(resp.body()));
}

// WebReactiveHttpClient.java 内容
@Override
public Mono<ReactiveHttpResponse<P>> executeRequest(ReactiveHttpRequest request) {
	return webClient.method(HttpMethod.valueOf(request.method()))
			.uri(request.uri())
			.headers(httpHeaders -> setUpHeaders(request, httpHeaders))
			.body(provideBody(request))
			.exchange()
			.onErrorMap(ex -> {
				Throwable errorMapped = errorMapper.apply(request, ex);
				if(errorMapped != null){
					return errorMapped;
				} else {
					return new ReactiveFeignException(ex, request);
				}
			})
			.map(response -> toReactiveHttpResponse(request, response));
}

总结

  1. 通过EnableFeignClients注解,开启Reactive Feign功能。
  2. Reactive Feign,通过动态代理,将Reactive Feign装配成Bean。
  3. 基于微服务调用时,先通过ReactiveLoadBalancer.choose,选择服务实例。
  4. 获取到服务实例后,通过WebClient,进行请求调用,并根据返回值不同,将结果通过Mono.from、Flux.from进行封装。
相关推荐
拉玛干2 小时前
社团周报系统可行性研究-web后端框架对比-springboot,django,gin
数据库·python·spring·golang
程序员大金5 小时前
基于SSM+Vue+MySQL的酒店管理系统
前端·vue.js·后端·mysql·spring·tomcat·mybatis
努力的布布5 小时前
Spring源码-从源码层面讲解声明式事务的运行流程
java·spring
程序员大金5 小时前
基于SpringBoot的旅游管理系统
java·vue.js·spring boot·后端·mysql·spring·旅游
爱上语文5 小时前
Springboot三层架构
java·开发语言·spring boot·spring·架构
你知道“铁甲小宝”吗丶7 小时前
【第33章】Spring Cloud之SkyWalking服务链路追踪
java·spring boot·spring·spring cloud·skywalking
听封7 小时前
Thymeleaf 的创建
java·spring boot·spring·maven
huapiaoy8 小时前
Spring mvc
java·spring·mvc
你知道“铁甲小宝”吗丶9 小时前
【第34章】Spring Cloud之SkyWalking分布式日志
java·spring boot·spring·spring cloud·skywalking