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进行封装。
相关推荐
paopaokaka_luck6 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
Yaml48 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
aloha_7898 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
wyh要好好学习10 小时前
SpringMVC快速上手
java·spring
尢词10 小时前
SpringMVC
java·spring·java-ee·tomcat·maven
wrx繁星点点11 小时前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
咕哧普拉啦13 小时前
乐尚代驾十订单支付seata、rabbitmq异步消息、redisson延迟队列
java·spring boot·mysql·spring·maven·乐尚代驾·java最新项目
wyh要好好学习13 小时前
SSM— spring,springMVC,mybatis整合
java·spring
A-bodgie14 小时前
Spring 中的 Environment 对象
java·后端·spring·servlet·springboot
桐桐桐14 小时前
Spring Security @PreAuthorize @PostAuthorize 权限控制
java·后端·spring