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进行封装。
相关推荐
暮乘白帝过重山16 分钟前
Singleton和Prototype的作用域与饿汉式/懒汉式的初始化方式
spring·原型模式·prototype·饿汉式·singleton·懒汉式
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
爱的叹息2 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
天上掉下来个程小白3 小时前
Redis-14.在Java中操作Redis-Spring Data Redis使用方式-操作列表类型的数据
java·redis·spring·springboot·苍穹外卖
汤姆大聪明3 小时前
Redisson 操作 Redis Stream 消息队列详解及实战案例
redis·spring·缓存·maven
正经摸鱼5 小时前
classpath与classpath*实现逻辑
后端·spring
良枫5 小时前
Spring Security认证授权深度解析
spring boot·spring