之前的章节提到过接口方法的解析,现在再看一下这个方法:
public Map<String, MethodHandler> apply(Target key) {
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
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);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
}
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
经过Contract类的parseAndValidatateMetadata(Class> targetType)方法解析过接口中的方法之后,会得出方法元数据MethodMetadata的集合,然后需要遍历这个集合,根据不同的条件创建RequestTemplate请求模板构建类BuildTemplateByResolvingArgs的实例,这里有三种:
interface Factory {
/**
* create a request template using args passed to a method invocation.
*/
RequestTemplate create(Object[] argv);
}
这个接口只有一个方法,就是创建RequestTemplate 请求模板
@Override
public RequestTemplate create(Object[] argv) {
RequestTemplate mutable = new RequestTemplate(metadata.template());
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.insert(0, String.valueOf(argv[urlIndex]));
}
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
if (indexToExpander.containsKey(i)) {
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}
RequestTemplate template = resolve(argv, mutable, varBuilder);
if (metadata.queryMapIndex() != null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
Object value = argv[metadata.queryMapIndex()];
Map<String, Object> queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}
if (metadata.headerMapIndex() != null) {
template = addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
}
return template;
}
分析如下:
private Map<String, Object> toQueryMap (Object value) {
if (value instanceof Map) {
return (Map<String, Object>)value;
}
try {
return queryMapEncoder.encode(value);
} catch (EncodeException e) {
throw new IllegalStateException(e);
}
}
如果被@QueryMap注解声明的对象是Map类型则直接返回,否则将被QueryMapEncoder对象编码成Map对象,Feign默认的QueryMapEncoder如下:
class Default implements QueryMapEncoder {
private final Map<Class<?>, ObjectParamMetadata> classToMetadata =
new HashMap<Class<?>, ObjectParamMetadata>();
@Override
public Map<String, Object> encode (Object object) throws EncodeException {
try {
ObjectParamMetadata metadata = getMetadata(object.getClass());
Map<String, Object> fieldNameToValue = new HashMap<String, Object>();
for (Field field : metadata.objectFields) {
Object value = field.get(object);
if (value != null && value != object) {
fieldNameToValue.put(field.getName(), value);
}
}
return fieldNameToValue;
} catch (IllegalAccessException e) {
throw new EncodeException("Failure encoding object into query map", e);
}
}
private ObjectParamMetadata getMetadata(Class<?> objectType) {
ObjectParamMetadata metadata = classToMetadata.get(objectType);
if (metadata == null) {
metadata = ObjectParamMetadata.parseObjectType(objectType);
classToMetadata.put(objectType, metadata);
}
return metadata;
}
private static class ObjectParamMetadata {
private final List<Field> objectFields;
private ObjectParamMetadata (List<Field> objectFields) {
this.objectFields = Collections.unmodifiableList(objectFields);
}
private static ObjectParamMetadata parseObjectType(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
for (Field field : type.getDeclaredFields()) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
fields.add(field);
}
return new ObjectParamMetadata(fields);
}
}
}
这个类主要就是利用反射解析class中的field,并做了一个小小的缓存,之后再填充参数到Map中
private RequestTemplate addQueryMapQueryParameters(Map<String, Object> queryMap, RequestTemplate mutable) {
for (Entry<String, Object> currEntry : queryMap.entrySet()) {
Collection<String> values = new ArrayList<String>();
boolean encoded = metadata.queryMapEncoded();
Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null : encoded ? nextObject.toString() : RequestTemplate.urlEncode(nextObject.toString()));
}
} else {
values.add(currValue == null ? null : encoded ? currValue.toString() : RequestTemplate.urlEncode(currValue.toString()));
}
mutable.query(true, encoded ? currEntry.getKey() : RequestTemplate.urlEncode(currEntry.getKey()), values);
}
return mutable;
}
遍历Map,根据@QueryMap的encoded参数来判断是否需要对value进行url encode,然后再通过RequestTemplate 的query方法将key-value放入queries中
private Object expandElements(Expander expander, Object value) {
if (value instanceof Iterable) {
return expandIterable(expander, (Iterable) value);
}
return expander.expand(value);
}
private List<String> expandIterable(Expander expander, Iterable value) {
List<String> values = new ArrayList<String>();
for (Object element : value) {
if (element!=null) {
values.add(expander.expand(element));
}
}
return values;
}
如果value属于Iterable类型(例如List),则调用expandIterable方法展开L并转换为String列表,转换的过程就是调用Expander 的expand方法转换为字符串,默认的是ToStringExpander,也就是调用value的toString方法直接转换
private RequestTemplate addHeaderMapHeaders(Map<String, Object> headerMap, RequestTemplate mutable) {
for (Entry<String, Object> currEntry : headerMap.entrySet()) {
Collection<String> values = new ArrayList<String>();
Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null : nextObject.toString());
}
} else {
values.add(currValue == null ? null : currValue.toString());
}
mutable.header(currEntry.getKey(), values);
}
return mutable;
}
这个处理过程跟@QueryMap差不多,就不赘述了,有一点需要注意的
protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable,
Map<String, Object> variables) {
// Resolving which variable names are already encoded using their indices
Map<String, Boolean> variableToEncoded = new LinkedHashMap<String, Boolean>();
for (Entry<Integer, Boolean> entry : metadata.indexToEncoded().entrySet()) {
Collection<String> names = metadata.indexToName().get(entry.getKey());
for (String name : names) {
variableToEncoded.put(name, entry.getValue());
}
}
return mutable.resolve(variables, variableToEncoded);
}
BuildTemplateByResolvingArgs的resolve方法只是简单的将需要encode的参数加入variableToEncoded集合,然后交由RequestTemplate进行处理
private static class BuildFormEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs {
private final Encoder encoder;
private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, QueryMapEncoder queryMapEncoder) {
super(metadata, queryMapEncoder);
this.encoder = encoder;
}
@Override
protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable,
Map<String, Object> variables) {
Map<String, Object> formVariables = new LinkedHashMap<String, Object>();
for (Entry<String, Object> entry : variables.entrySet()) {
if (metadata.formParams().contains(entry.getKey())) {
formVariables.put(entry.getKey(), entry.getValue());
}
}
try {
encoder.encode(formVariables, Encoder.MAP_STRING_WILDCARD, mutable);
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
return super.resolve(argv, mutable, variables);
}
}
这个类是继承自BuildTemplateByResolvingArgs,重写了父类的resolve方法,在@Param声明的参数列表中找出属于form的数据,然后再encode放入RequestTemplate的body当中,然后再调用父类的resolve方法处理其他参数
class Default implements Encoder {
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
if (bodyType == String.class) {
template.body(object.toString());
} else if (bodyType == byte[].class) {
template.body((byte[]) object, null);
} else if (object != null) {
throw new EncodeException(
format("%s is not a type supported by this encoder.", object.getClass()));
}
}
}
这里需要注意一下,Feign默认的Encoder只处理String 和 byte[] 类型的如果是其他类型并且参数不为null时将抛出EncodeException异常,当然,Feign也集成了一些第三方的Json处理包,比如Jackson和Gson等都可以处理这类数据
private static class BuildEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs {
private final Encoder encoder;
private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder, QueryMapEncoder queryMapEncoder) {
super(metadata, queryMapEncoder);
this.encoder = encoder;
}
@Override
protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable,
Map<String, Object> variables) {
Object body = argv[metadata.bodyIndex()];
checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex());
try {
encoder.encode(body, metadata.bodyType(), mutable);
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
return super.resolve(argv, mutable, variables);
}
}
这个类是继承自BuildTemplateByResolvingArgs,重写了父类的resolve方法,在参数列表中找出属于body的数据(之前提到过,没有被@Param、@QueryMap、@HeaderMap注解声明过,并且不是URI类型的参数的就属于body类型参数,而且body类型参数不能与form类型参数共存并且只能有一个body类型参数,否则会报错),然后再encode放入RequestTemplate的body当中,然后再调用父类的resolve方法处理其他参数