博主阅读代码的版本是io.github.openfeign:9.7.0、spring-cloud-openfeign:2.0.2.RELEASE
一、Feign的协议接口-Contract
package feign;
public interface Contract {
//将类解析并验证生成MethodMetadataList
List<MethodMetadata> parseAndValidatateMetadata(Class<?> var1);
...
}
Contract类是在io.github.openfeign:feign-core依赖包,是一个接口,仅有一个需要实现的方法,将类解析并验证生成MethodMetadataList。
package feign;
//方法元数据类
public final class MethodMetadata implements Serializable {
private static final long serialVersionUID = 1L;
private String configKey;
//http请求的返回类型
private transient Type returnType;
//该方法中第几个参数作为http请求的url
private Integer urlIndex;
//该方法中第几个参数作为http请求的body
private Integer bodyIndex;
//该方法中第几个参数作为http请求的键值对类型的header
private Integer headerMapIndex;
//该方法中第几个参数作为http请求的键值对类型的query
private Integer queryMapIndex;
private boolean queryMapEncoded;
//http请求body的类型
private transient Type bodyType;
//http请求模板 重点
private RequestTemplate template = new RequestTemplate();
private List<String> formParams = new ArrayList();
private Map<Integer, Collection<String>> indexToName = new LinkedHashMap();
private Map<Integer, Class<? extends Expander>> indexToExpanderClass = new LinkedHashMap();
private Map<Integer, Boolean> indexToEncoded = new LinkedHashMap();
private transient Map<Integer, Expander> indexToExpander;
...
}
package feign;
public final class RequestTemplate implements Serializable {
private static final long serialVersionUID = 1L;
//http请求键值对类型的query参数
private final Map<String, Collection<String>> queries = new LinkedHashMap();
//http请求键值对类型的headers
private final Map<String, Collection<String>> headers = new LinkedHashMap();
//请求方式
private String method;
//请求url
private StringBuilder url = new StringBuilder();
//编码方式
private transient Charset charset;
//http请求body
private byte[] body;
//http请求字符串类型的body
private String bodyTemplate;
private boolean decodeSlash = true;
private CollectionFormat collectionFormat;
...
}
MethodMetadata类是一个承上启下的类有点不太好理解,RequestTemplate类是一个http请求的封装类,包含必要的url、method、header、body和常见的queries(url?后面&方式拼接的参数)等属性,其对象是一个最终要发起的http请求。
Contract类将一个clazz(Class类型的参数)解析成多个MethodMetadata。因为一个clazz会有多个method,所以会解析成多个MethodMetadata对象,每个MethodMetadata对象都一个RequestTemplate对象。RequestTemplate对象是一个最终被调用的请求。结合MethodMetadata代码看,就是clazz的方法与最终要发起的http请求属性之间的映射。
Feign的协议Contract就是将一个类的各个方法(暂定义目标方法)解析成不同的RequestTemplate,MethodMetadata只是一个过程对象,MethodMetadata对象urlIndex、bodyIndex、headerMapIndex、queryMapIndex都会在发起请求之前把目标方法参数里的值设置到RequestTemplate相应的属性里。
二、Feign的注解抽象协议-BaseContract
package feign;
public abstract static class BaseContract implements Contract {
//实现Contract接口方法
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
Util.checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", new Object[]{targetType.getSimpleName()});
Util.checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s", new Object[]{targetType.getSimpleName()});
if (targetType.getInterfaces().length == 1) {
Util.checkState(targetType.getInterfaces()[0].getInterfaces().length == 0, "Only single-level inheritance supported: %s", new Object[]{targetType.getSimpleName()});
}
//初始化解析结果
Map<String, MethodMetadata> result = new LinkedHashMap();
//获取clazz的目标方法
Method[] var3 = targetType.getMethods();
int var4 = var3.length;
//遍历目标方法
for(int var5 = 0; var5 < var4; ++var5) {
Method method = var3[var5];
//对方法做过滤 1、不是Object类的方法 2、修饰符为public、private、protect方法 3、不是 interface的且修饰符为default方法
if (method.getDeclaringClass() != Object.class && (method.getModifiers() & 8) == 0 && !Util.isDefault(method)) {
//调用单个目标方法的解析方法
MethodMetadata metadata = this.parseAndValidateMetadata(targetType, method);
Util.checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s", new Object[]{metadata.configKey()});
result.put(metadata.configKey(), metadata);
}
}
return new ArrayList(result.values());
}
//单个目标方法的解析
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method)
{
//初始化返回
MethodMetadata data = new MethodMetadata();
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
data.configKey(Feign.configKey(targetType, method));
if (targetType.getInterfaces().length == 1) {
this.processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
//根据类上面的注解设置MethodMetadata属性
this.processAnnotationOnClass(data, targetType);
Annotation[] var4 = method.getAnnotations();
int var5 = var4.length;
//根据方法上面的注解设置MethodMetadata属性
for(int var6 = 0; var6 < var5; ++var6) {
Annotation methodAnnotation = var4[var6];
this.processAnnotationOnMethod(data, methodAnnotation, method);
}
Util.checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)", new Object[]{method.getName()});
Class<?>[] parameterTypes = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
int count = parameterAnnotations.length;
//根据方法参数上面的注解设置MethodMetadata属性
for(int i = 0; i < count; ++i) {
boolean isHttpAnnotation = false;
if (parameterAnnotations[i] != null) {
isHttpAnnotation = this.processAnnotationsOnParameter(data, parameterAnnotations[i], i);
}
if (parameterTypes[i] == URI.class) {
data.urlIndex(i);
} else if (!isHttpAnnotation) {
Util.checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters.", new Object[0]);
Util.checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", new Object[]{method});
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 && Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
}
return data;
}
//根据类上面的注解设置MethodMetadata属性
protected abstract void processAnnotationOnClass(MethodMetadata var1, Class<?> var2);
//根据方法上面的注解设置MethodMetadata属性
protected abstract void processAnnotationOnMethod(MethodMetadata var1, Annotation var2, Method var3);
//根据方法参数上面的注解设置MethodMetadata属性
protected abstract boolean processAnnotationsOnParameter(MethodMetadata var1, Annotation[] var2, int var3);
}
BaseContract是Contract的内部静态抽象类,实现了Contract接口,对目标类做了一些验证,后面将整个解析步骤分解为一下步。
(1)解析类的注解。
(2)解析方法的注解。
(3)解析方法参数上的注解。
三、Spring-cloud-Feign的协议-SpringMvcContract
package org.springframework.cloud.openfeign.support;
public class SpringMvcContract extends BaseContract implements ResourceLoaderAware {
private static final String ACCEPT = "Accept";
private static final String CONTENT_TYPE = "Content-Type";
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
private final Map<String, Method> processedMethods;
private final ConversionService conversionService;
private final Expander expander;
private ResourceLoader resourceLoader;
//AnnotatedParameterProcessors的构造方法
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
this(annotatedParameterProcessors, new DefaultConversionService());
}
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
if (clz.getInterfaces().length == 0) {
//获取类上面的@RequestMapping注解
RequestMapping classAnnotation = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(clz, RequestMapping.class);
if (classAnnotation != null && classAnnotation.value().length > 0) {
String pathValue = Util.emptyToNull(classAnnotation.value()[0]);
pathValue = this.resolve(pathValue);
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
//将@RequestMapping注解的值插入到MethodMetadata的RequesTemplate的url里
data.template().insert(0, pathValue);
}
}
}
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
//判断方法上的注解是否是@RequestMapping 或者 注解上包含了@RequestMapping 例如@GetMapping等
if (RequestMapping.class.isInstance(methodAnnotation) || methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
RequestMapping methodMapping = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
//获取注解的请求方式
RequestMethod[] methods = methodMapping.method();
if (methods.length == 0) {
methods = new RequestMethod[]{RequestMethod.GET};
}
this.checkOne(method, methods, "method");
//设置MethodMetadata的RequesTemplat的method
data.template().method(methods[0].name());
this.checkAtMostOne(method, methodMapping.value(), "value");
if (methodMapping.value().length > 0) {
String pathValue = Util.emptyToNull(methodMapping.value()[0]);
if (pathValue != null) {
pathValue = this.resolve(pathValue);
if (!pathValue.startsWith("/") && !data.template().toString().endsWith("/")) {
pathValue = "/" + pathValue;
}
//将注解的vaue拼接到MethodMetadata的RequesTemplate的url后面
data.template().append(pathValue);
}
}
//通过方法上@Produces注解设置MethodMetadata的RequesTemplate的header中Accept值
this.parseProduces(data, method, methodMapping);
//通过方法上@Produces注解设置MethodMetadata的RequesTemplate的header中Content-Type值
this.parseConsumes(data, method, methodMapping);
//通过方法上@Produces注解设置MethodMetadata的RequesTemplate的header
this.parseHeaders(data, method, methodMapping);
data.indexToExpander(new LinkedHashMap());
}
}
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
boolean isHttpAnnotation = false;
AnnotatedParameterContext context = new SpringMvcContract.SimpleAnnotatedParameterContext(data, paramIndex);
Method method = (Method)this.processedMethods.get(data.configKey());
Annotation[] var7 = annotations;
int var8 = annotations.length;
for(int var9 = 0; var9 < var8; ++var9) {
Annotation parameterAnnotation = var7[var9];
//通过AnnotatedParameterProcessor解析方法参数上的注解
AnnotatedParameterProcessor processor = (AnnotatedParameterProcessor)this.annotatedArgumentProcessors.get(parameterAnnotation.annotationType());
if (processor != null) {
Annotation processParameterAnnotation = this.synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation, method, paramIndex);
isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);
}
}
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null && this.conversionService.canConvert(method.getParameterTypes()[paramIndex], String.class)) {
data.indexToExpander().put(paramIndex, this.expander);
}
return isHttpAnnotation;
}
}
spring-cloud-openfeign:2.0.2.RELEASE仅有3个AnnotatedParameterProcessor。
(1)PathVariableParameterProcessor
package org.springframework.cloud.openfeign.annotation;
public class PathVariableParameterProcessor implements AnnotatedParameterProcessor {
//支持@PathVariable注解
private static final Class<PathVariable> ANNOTATION = PathVariable.class;
public PathVariableParameterProcessor() {
}
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
//获取@PathVariable注解的value
String name = ((PathVariable)ANNOTATION.cast(annotation)).value();
Util.checkState(Util.emptyToNull(name) != null, "PathVariable annotation was empty on param %s.", new Object[]{context.getParameterIndex()});
context.setParameterName(name);
MethodMetadata data = context.getMethodMetadata();
String varName = '{' + name + '}';
if (!data.template().url().contains(varName) && !this.searchMapValues(data.template().queries(), varName) && !this.searchMapValues(data.template().headers(), varName)) {
//将name设置到MethodMetadata的RequesTemplate的form表单参数里面? 博主还有点疑问
data.formParams().add(name);
}
return true;
}
(2)RequestHeaderParameterProcessor
package org.springframework.cloud.openfeign.annotation;
public class RequestHeaderParameterProcessor implements AnnotatedParameterProcessor {
//支持@RequestHeader注解
private static final Class<RequestHeader> ANNOTATION = RequestHeader.class;
public RequestHeaderParameterProcessor() {
}
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
//如果加了@RequestHeader的参数类型是Map
if (Map.class.isAssignableFrom(parameterType)) {
Util.checkState(data.headerMapIndex() == null, "Header map can only be present once.", new Object[0]);
//设置MethodMetadata的RequesTemplate的键值对类型的header参数下标
data.headerMapIndex(parameterIndex);
return true;
} else {
//获取@RequestHeader的name
String name = ((RequestHeader)ANNOTATION.cast(annotation)).value();
Util.checkState(Util.emptyToNull(name) != null, "RequestHeader.value() was empty on parameter %s", new Object[]{parameterIndex});
context.setParameterName(name);
Collection<String> header = context.setTemplateParameter(name, (Collection)data.template().headers().get(name));
//设置MethodMetadata的RequesTemplate的键值对类型的header单个属性
data.template().header(name, header);
return true;
}
}
}
(3)RequestParamParameterProcessor
package org.springframework.cloud.openfeign.annotation;
public class RequestParamParameterProcessor implements AnnotatedParameterProcessor {
//支持@RequestParam注解
private static final Class<RequestParam> ANNOTATION = RequestParam.class;
public RequestParamParameterProcessor() {
}
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
//如果加了@RequestParam的参数类型是Map
if (Map.class.isAssignableFrom(parameterType)) {
Util.checkState(data.queryMapIndex() == null, "Query map can only be present once.", new Object[0]);
//设置MethodMetadata的RequesTemplate的键值对类型的query参数下标
data.queryMapIndex(parameterIndex);
return true;
} else {
RequestParam requestParam = (RequestParam)ANNOTATION.cast(annotation);
String name = requestParam.value();
Util.checkState(Util.emptyToNull(name) != null, "RequestParam.value() was empty on parameter %s", new Object[]{parameterIndex});
context.setParameterName(name);
Collection<String> query = context.setTemplateParameter(name, (Collection)data.template().queries().get(name));
//设置MethodMetadata的RequesTemplate的键值对类型的query单个属性
data.template().query(name, query);
return true;
}
}
}
SpringMvcContract继承了BaseContract类,重写了以下三个方法。
(1)processAnnotationOnClass 解析类的注解的方法。
支持类上的@RequestMapping注解
(2)processAnnotationOnMethod 解析方法的注解。
支持方法上的@RequestMapping注解、以及@GetMapping、@PostMapping等包含了@RequestMapping的注解
(3)processAnnotationsOnParameter 解析方法参数上的注解。
支持方法参数上的@PathVariable、@RequestHeader、@RequestParam注解
四、总结
io.github.openfeign:9.7.0、spring-cloud-openfeign:2.0.2.RELEASE版本有点旧了,但是在新版本中的实现不会有太多的改变。还有只需要实现一个AnnotatedParameterProcessor,再定义一个SpringMvcContract的bean,将自定义的AnnotatedParameterProcessor传入构造方法,feignClient就支持自定义的注解。