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接口信息,由于对这些类不够熟悉,所以我们初次看起来可能会觉得有些懵。希望通过本文的讲解,大家都能有所收获~
领取专属 10元无门槛券
私享最新 技术干货