首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

openfeign的使用原理(二)

1、前言

在上一篇文章中,我们谈了openfeign借助一系列自动配置类帮我们自动创建feign client对象的一个过程,在文章最后,我们也提到,openfeign帮我们创建的feign client对象其实是一个代理对象,本篇我们将继续探讨下feign client代理对象的生成过程,由于相关代码不算太难理解,本篇文字并不多,主要是以展示源码内容为准。

2、原理讲解

我们从下列代码开始看起,其中build方法返回的是一个ReflectiveFeign对象。

//Feign.class

public <T> T target(Target<T> target) {

return build().newInstance(target);

}

ReflectiveFeign类的newInstance方法定义如下:

// ReflectiveFeign.class

/**

* creates an api binding to the {@code target}. As this invokes reflection, care should be taken

* to cache the result.

*/

@SuppressWarnings("unchecked")

@Override

public <T> T newInstance(Target<T> target) {

//重点,对接口方法标记了@RequestMapping(GetMapping、PostMapping等等)的方法进行提取转换

Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);

Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();

List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

for (Method method : target.type().getMethods()) {

if (method.getDeclaringClass() == Object.class) {

continue;

} else if (Util.isDefault(method)) {

DefaultMethodHandler handler = new DefaultMethodHandler(method);

defaultMethodHandlers.add(handler);

methodToHandler.put(method, handler);

} else {

methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));

}

}

InvocationHandler handler = factory.create(target, methodToHandler);

T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),

new Class<?>[] {target.type()}, handler);

for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {

defaultMethodHandler.bindTo(proxy);

}

return proxy;

}2.1、ParseHandlersByName.apply方法

ParseHandlersByName是ReflectiveFeign的一个静态内部类,它是根据接口方法名来创建对应的转换方法处理器。在创建ReflectiveFeign对象时,ReflectiveFeign的构造函数会接收一个ParseHandlersByName对象。我们来看下它的apply方法。

//ReflectiveFeign.ParseHandlersByName类

public Map<String, MethodHandler> apply(Target target) {

//contract指的是SpringMvcContract对象,此代码获取接口里面所有的表示http请求的方法的元数据

List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());

//为每个方法生成相应的MethodHandler

Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();

for (MethodMetadata md : metadata) {

BuildTemplateByResolvingArgs buildTemplate;

if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {

buildTemplate =

new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);

} else if (md.bodyIndex() != null || md.alwaysEncodeBody()) {

buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);

} else {

buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);

}

if (md.isIgnored()) {

result.put(md.configKey(), args -> {

throw new IllegalStateException(md.configKey() + " is not a method handled by feign");

});

} else {

result.put(md.configKey(),

factory.create(target, md, buildTemplate, options, decoder, errorDecoder));

}

}

return result;

}

我们看下contract.parseAndValidateMetadata方法的实现:

//Contract类

@Override

public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {

checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s",

targetType.getSimpleName());

//这里限制了targetType类对象指代的类或接口最多只能继承一个接口

checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s",

targetType.getSimpleName());

final Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();

for (final Method method : targetType.getMethods()) {

if (method.getDeclaringClass() == Object.class ||

(method.getModifiers() & Modifier.STATIC) != 0 ||

Util.isDefault(method)) {

continue;

}

/生成方法的元数据信息的关键点

final MethodMetadata metadata = parseAndValidateMetadata(targetType, method);

if (result.containsKey(metadata.configKey())) {

MethodMetadata existingMetadata = result.get(metadata.configKey());

Type existingReturnType = existingMetadata.returnType();

Type overridingReturnType = metadata.returnType();

Type resolvedType = Types.resolveReturnType(existingReturnType, overridingReturnType);

if (resolvedType.equals(overridingReturnType)) {

result.put(metadata.configKey(), metadata);

}

continue;

}

result.put(metadata.configKey(), metadata);

}

return new ArrayList<>(result.values());

}2.1.1、SpringMvcContract类的parseAndValidateMetadata方法

parseAndValidateMetadata方法完成了对Feign接口内每个方法的元数据信息的提取工作。我们进入SpringMvcContract类看一下parseAndValidateMetadata(targetType, method)的实现:

//SpringMvcContract类

@Override

public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {

//记录下需要处理的接口方法

processedMethods.put(Feign.configKey(targetType, method), method);

//调用父类BaseContract中的parseAndValidateMetadata,提取方法的元数据信息

return super.parseAndValidateMetadata(targetType, method);

}

BaseContract类,是Contract的子类,也是Contract类的一个内部抽象类,它的parseAndValidateMetadata方法对接口方法进行了剖析,提取出了和Http接口信息相关的信息,其实也就是springmvc中开发controller用到的那些注解。

//BaseContract类

protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {

final MethodMetadata data = new MethodMetadata();

data.targetType(targetType);

data.method(method);

data.returnType(

Types.resolve(targetType, targetType, method.getGenericReturnType()));

//configkey,用来作为方法元数据对象的一个标识,是“类型#方法名(方法参数类型列表)”的形式

data.configKey(Feign.configKey(targetType, method));

if (AlwaysEncodeBodyContract.class.isAssignableFrom(this.getClass())) {

data.alwaysEncodeBody(true);

}

//对targetType继承的接口进行处理,前提是只继承了一个接口

if (targetType.getInterfaces().length == 1) {

processAnnotationOnClass(data, targetType.getInterfaces()[0]);

}

//处理当前接口上面的注解,也就是targetType类对象所属的接口

processAnnotationOnClass(data, targetType);

//对方法上的注解进行遍历处理

for (final Annotation methodAnnotation : method.getAnnotations()) {

processAnnotationOnMethod(data, methodAnnotation, method);

}

if (data.isIgnored()) {

return data;

}

checkState(data.template().method() != null,

"Method %s not annotated with HTTP method type (ex. GET, POST)%s",

data.configKey(), data.warnings());

final Class<?>[] parameterTypes = method.getParameterTypes();

final Type[] genericParameterTypes = method.getGenericParameterTypes();

//处理方法参数上面的注解

final Annotation[][] parameterAnnotations = method.getParameterAnnotations();

final int count = parameterAnnotations.length;

for (int i = 0; i < count; i++) {

boolean isHttpAnnotation = false;

if (parameterAnnotations[i] != null) {

isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);

}

if (isHttpAnnotation) {

data.ignoreParamater(i);

}

if (parameterTypes[i] == URI.class) {

data.urlIndex(i);

} else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {

if (data.isAlreadyProcessed(i)) {

checkState(data.formParams().isEmpty() || data.bodyIndex() == null,

"Body parameters cannot be used with form parameters.%s", data.warnings());

} else if (!data.alwaysEncodeBody()) {

checkState(data.formParams().isEmpty(),

"Body parameters cannot be used with form parameters.%s", data.warnings());

checkState(data.bodyIndex() == null,

"Method has too many Body parameters: %s%s", method, data.warnings());

data.bodyIndex(i);

data.bodyType(

Types.resolve(targetType, targetType, genericParameterTypes[i]));

}

}

}

if (data.headerMapIndex() != null) {

checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()],

genericParameterTypes[data.headerMapIndex()]);

}

if (data.queryMapIndex() != null) {

if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {

checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);

}

}

return data;

}2.1.1.1、MethodMetaData中的configkey

每个接口方法都会有一个configKey属性,方便后续根据它来获取到MethodMetaData。

//configkey,用来作为方法元数据对象的一个标识,是“类型#方法名(方法参数类型列表)”的形式

data.configKey(Feign.configKey(targetType, method));

我们看下Feign.configKey方法的内部实现:

//Feign类

public static String configKey(Class targetType, Method method) {

StringBuilder builder = new StringBuilder();

builder.append(targetType.getSimpleName());

builder.append('#').append(method.getName()).append('(');

for (Type param : method.getGenericParameterTypes()) {

param = Types.resolve(targetType, targetType, param);

builder.append(Types.getRawType(param).getSimpleName()).append(',');

}

if (method.getParameterTypes().length > 0) {

builder.deleteCharAt(builder.length() - 1);

}

return builder.append(')').toString();

}

由上面代码可知,每个方法对应的configKey的值就是类似“类型#方法名(方法参数类型列表)”的形式,拿我们写的OrderClient接口中的findById方法来说,它的configKey的值就是"OrderClient#findById(Long)"。

2.1.1.2、processAnnotationOnClass方法

processAnnotationOnClass方法的主要作用只有一个,就是不允许@FeignClient所在的接口(包括父接口)上面存在@RequestMapping注解(GetMapping、PostMapping等也算),不然就会报错,导致系统无法启动。

//SpringMvcContract类

@Override

protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {

RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);

if (classAnnotation != null) {

LOG.error("Cannot process class: " + clz.getName()

+ ". @RequestMapping annotation is not allowed on @FeignClient interfaces.");

throw new IllegalArgumentException("@RequestMapping annotation not allowed on @FeignClient interfaces");

}

CollectionFormat collectionFormat = findMergedAnnotation(clz, CollectionFormat.class);

if (collectionFormat != null) {

data.template().collectionFormat(collectionFormat.value());

}

}2.1.1.3、processAnnotationOnMethod方法

处理完接口上面的注解之后,接下来就要遍历处理接口内部方法上面所有的注解,完成这一功能的就是processAnnotationOnMethod方法,我们来看下它的内部实现:

//SpringMvcContract类

@Override

protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {

if (CollectionFormat.class.isInstance(methodAnnotation)) {

CollectionFormat collectionFormat = findMergedAnnotation(method, CollectionFormat.class);

data.template().collectionFormat(collectionFormat.value());

}

if (!RequestMapping.class.isInstance(methodAnnotation)

&& !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {

return;

}

RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);

// HTTP Method

RequestMethod[] methods = methodMapping.method();

if (methods.length == 0) {

methods = new RequestMethod[] { RequestMethod.GET };

}

checkOne(method, methods, "method");

data.template().method(Request.HttpMethod.valueOf(methods[0].name()));

// path

checkAtMostOne(method, methodMapping.value(), "value");

if (methodMapping.value().length > 0) {

String pathValue = emptyToNull(methodMapping.value()[0]);

if (pathValue != null) {

pathValue = resolve(pathValue);

// Append path from @RequestMapping if value is present on method

if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {

pathValue = "/" + pathValue;

}

data.template().uri(pathValue, true);

if (data.template().decodeSlash() != decodeSlash) {

data.template().decodeSlash(decodeSlash);

}

}

}

// produces

parseProduces(data, method, methodMapping);

// consumes

parseConsumes(data, method, methodMapping);

// headers

parseHeaders(data, method, methodMapping);

data.indexToExpander(new LinkedHashMap<>());

}

上面的方法很简单,主要就是处理接口方法上面的RequestMapping注解,包括提取path、header等信息。如果当前注解不是RequestMapping的实例,则会跳过当前注解,转而处理方法上面的下一个注解。

2.1.1.4、processAnnotationsOnParameter方法

处理完方法上面的所有注解之后,就是处理该方法的参数上面的注解,完成这一功能的是processAnnotationsOnParameter方法,我们来看下它的内部实现:

//SpringMvcContract类

@Override

protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {

boolean isHttpAnnotation = false;

AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(data,

paramIndex);

Method method = processedMethods.get(data.configKey());

for (Annotation parameterAnnotation : annotations) {

//根据当前的参数注解获取对应的注解参数处理器对象

AnnotatedParameterProcessor processor = annotatedArgumentProcessors

.get(parameterAnnotation.annotationType());

if (processor != null) {

Annotation processParameterAnnotation;

// synthesize, handling @AliasFor, while falling back to parameter name on

// missing String #value():

processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation,

method, paramIndex);

isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);

}

}

if (!isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {

TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);

if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {

Param.Expander expander = convertingExpanderFactory.getExpander(typeDescriptor);

if (expander != null) {

data.indexToExpander().put(paramIndex, expander);

}

}

}

return isHttpAnnotation;

}

上面方法对方法参数注解处理的过程中用到了annotatedArgumentProcessors对象,它是map类型的,是在创建

SpringMvcContract对象(由FeignClientsConfiguration配置类创建)的时候生成的,map中存储的是openfeign提供的一些默认的注解参数处理器对象。核心代码如下:

//SpringMvcContract类

public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,

ConversionService conversionService, boolean decodeSlash) {

Assert.notNull(annotatedParameterProcessors, "Parameter processors can not be null.");

Assert.notNull(conversionService, "ConversionService can not be null.");

//获取默认的注解参数处理器

List<AnnotatedParameterProcessor> processors = getDefaultAnnotatedArgumentsProcessors();

processors.addAll(annotatedParameterProcessors);

//将list格式的注解参数处理器转换为map形式

annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);

this.conversionService = conversionService;

convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);

this.decodeSlash = decodeSlash;

}

private List<AnnotatedParameterProcessor> getDefaultAnnotatedArgumentsProcessors() {

List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>();

annotatedArgumentResolvers.add(new MatrixVariableParameterProcessor());

annotatedArgumentResolvers.add(new PathVariableParameterProcessor());

annotatedArgumentResolvers.add(new RequestParamParameterProcessor());

annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());

annotatedArgumentResolvers.add(new QueryMapParameterProcessor());

annotatedArgumentResolvers.add(new RequestPartParameterProcessor());

annotatedArgumentResolvers.add(new CookieValueParameterProcessor());

return annotatedArgumentResolvers;

}

private Map<Class<? extends Annotation>, AnnotatedParameterProcessor> toAnnotatedArgumentProcessorMap(

List<AnnotatedParameterProcessor> processors) {

Map<Class<? extends Annotation>, AnnotatedParameterProcessor> result = new HashMap<>();

for (AnnotatedParameterProcessor processor : processors) {

result.put(processor.getAnnotationType(), processor);

}

return result;

}

每个处理器都声明了自己能处理的注解类型,比如PathVariableParameterProcessor只能处理@PathVariable注解:

public class PathVariableParameterProcessor implements AnnotatedParameterProcessor {

private static final Class<PathVariable> ANNOTATION = PathVariable.class;

public PathVariableParameterProcessor() {

}

public Class<? extends Annotation> getAnnotationType() {

return ANNOTATION;

}

。。。

}2.2.1、为每个方法生成对应的MethodHandler

我们继续看apply方法,该方法后面有这么一段代码:

result.put(md.configKey(),

factory.create(target, md, buildTemplate, options, decoder, errorDecoder));

其中factory.create方法返回的是一个SynchronousMethodHandler对象,它的核心方法如下:

//SynchronousMethodHandler类

@Override

public Object invoke(Object[] argv) throws Throwable {

RequestTemplate template = buildTemplateFromArgs.create(argv);

Options options = findOptions(argv);

Retryer retryer = this.retryer.clone();

while (true) {

try {

//执行http接口调用并对返回结果进行解码

return executeAndDecode(template, options);

} catch (RetryableException e) {

try {

retryer.continueOrPropagate(e);

} catch (RetryableException th) {

Throwable cause = th.getCause();

if (propagationPolicy == UNWRAP && cause != null) {

throw cause;

} else {

throw th;

}

}

if (logLevel != Logger.Level.NONE) {

logger.logRetry(metadata.configKey(), logLevel);

}

continue;

}

}

}

我们调用Feign接口,最终都会交由SynchronousMethodHandler的invoke来执行。

2.2、生成Method->MethodHandler的map对象

2.1部分我们得到了以configKey为key,MethodHandler为value的map对象,我们通过反射,可以直接拿到Method对象,因此为方便查找,openfeign又生成了一个Method作为key,MethodHandler作为value的map对象:

//ReflectiveFeign类

@Override

public <T> T newInstance(Target<T> target) {

...

for (Method method : target.type().getMethods()) {

if (method.getDeclaringClass() == Object.class) {

continue;

} else if (Util.isDefault(method)) {

DefaultMethodHandler handler = new DefaultMethodHandler(method);

defaultMethodHandlers.add(handler);

methodToHandler.put(method, handler);

} else {

methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));

}

}

...

}2.3、为当前Feign接口生成代理对象

现在走到了创建Feign实例对象的最后一步,创建代理对象:

//ReflectiveFeign类

@Override

public <T> T newInstance(Target<T> target) {

...

//ReflectiveFeign.FeignInvocationHandler类型的对象

InvocationHandler handler = factory.create(target, methodToHandler);

T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),

new Class<?>[] {target.type()}, handler);

for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {

defaultMethodHandler.bindTo(proxy);

}

return proxy;

}

factory.create方法返回的是ReflectiveFeign.FeignInvocationHandler类型的对象,FeignInvocationHandler实现了InvocationHandler接口,是ReflectiveFeign的一个静态内部类,它的内部实现如下:

// ReflectiveFeign类

static class FeignInvocationHandler implements InvocationHandler {

private final Target target;

private final Map<Method, MethodHandler> dispatch;

FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {

this.target = checkNotNull(target, "target");

this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

if ("equals".equals(method.getName())) {

try {

Object otherHandler =

args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;

return equals(otherHandler);

} catch (IllegalArgumentException e) {

return false;

}

} else if ("hashCode".equals(method.getName())) {

return hashCode();

} else if ("toString".equals(method.getName())) {

return toString();

}

return dispatch.get(method).invoke(args);

}

@Override

public boolean equals(Object obj) {

if (obj instanceof FeignInvocationHandler) {

FeignInvocationHandler other = (FeignInvocationHandler) obj;

return target.equals(other.target);

}

return false;

}

@Override

public int hashCode() {

return target.hashCode();

}

@Override

public String toString() {

return target.toString();

}

}

Proxy.newProxyInstance方法通过jdk动态代理来创建Feign接口的代理对象,当我们调用Feign实例对象的某个方法时,都会交由上面的FeignInvocationHandler对象进行处理,然后选择相应的MethodHandler,发起http接口调用。

3、小结

本节我们主要看了openfeign根据Feign接口创建Feign代理对象的过程,其实内部的逻辑还是比较简单的,无非就是解析方法上面的RequestMapping注解,提取请求接口的路径信息,还有解析接口方法的参数上的注解,只不过openfeign自己定义了一些类来处理常用的http接口信息,由于对这些类不够熟悉,所以我们初次看起来可能会觉得有些懵。希望通过本文的讲解,大家都能有所收获~

  • 发表于:
  • 原文链接https://page.om.qq.com/page/O4Qxrqu_FneOXfHAuxywoI1w0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券