声明式接口调用:Feign源码剖析(2)基于JDK动态代理构建FeignClient的过程
上文我们已经分析到
FeignClientFactoryBean#getTarget()
方法,这里完成了FeignClient动态代理的创建。但并未分析具体的创建过程,本文将深入Feign Core将FeignClient到底是如何被创建出来的给摸清楚。
1 | class HystrixTargeter implements Targeter { |
- 我们此时并未整合Hystrix,所以此处的feign还是
Feign.Builder
,此处直接走他的target方法
1 | public abstract class Feign { |
SynchronousMethodHandler
同步方法处理器invocationHandlerFactory
invocationHandler这东西怎么这么眼熟呢,这不是JDK动态代理那个套路里的接口吗?我们一探究竟1
2
3
4
5
6
7static final class Default implements InvocationHandlerFactory {
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}这包裹的层次够深的,我们看看这个
FeignInvoccationHandler
是啥1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28static 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);
}
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);
}到此,终于真相大白,验证了我们根据命名得到的猜测,这确实是使用了JDK的动态代理技术,这个invoke方法我们后续再分析,其实也不难推测,无非是拦截对ServiceAClient的方法的调用,委托到这个
FeignInvocationHandler#invoke()
,然后执行feign的逻辑,最后就是调用底层的一个HTTP客户端,根据我们的RequestMapping
等注解里的参数拼接一个HTTP Request,发送出去罢了
接上
build().newInstance(target)
,build()
出了一个ReflectiveFeign
对象,然后调用它的newInstance()
方法,在这里,最终完成了基于JDK动态代理的FeignClient的创建
柳暗花明,看ReflectiveFeign#newInstance()
1 | class ReflectiveFeign |
下面的部分是各个步骤的详细剖析,没兴趣的同学可以不看了,在这里已经完成了所有关于FeignClient创建的核心知识了。
nameToHandler
看名字就可以猜测是什么东西的名称和代理方法处理器的映射1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class ReflectiveFeign
public Map<String, MethodHandler> apply(Target key) {
// 这里执行了SpringMvcContract组件的解析SpringMVC注解的逻辑,最后解析后组成了一个List
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.parseAndValidateMetadata
这里就轮到SpringMvcContract组件闪亮登场了,他负责对ReqeustMapping
、PathVaraiable
等注解的解析1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20abstract class BaseContract implements Contract {
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
// 使用反射,遍历ServiceAClient的每个方法
for (Method method : targetType.getMethods()) {
if (method.getDeclaringClass() == Object.class ||
(method.getModifiers() & Modifier.STATIC) != 0 ||
Util.isDefault(method)) {
continue;
}
// 调用SpringMvcContract来具体处理注解
MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
result.put(metadata.configKey(), metadata);
}
return new ArrayList<>(result.values());
}通过反射遍历ServiceAClient中的每个方法,然后委托给
SpringMvcContract#parseValidateMetadata
处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27class SpringMvcContract extends Contract.BaseContract
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
this.processedMethods.put(Feign.configKey(targetType, method), method);
// 又调回父类方法了
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
RequestMapping classAnnotation = findMergedAnnotation(targetType,
RequestMapping.class);
if (classAnnotation != null) {
// produces - use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(ACCEPT)) {
parseProduces(md, method, classAnnotation);
}
// consumes -- use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
parseConsumes(md, method, classAnnotation);
}
// headers -- class annotation is inherited to methods, always write these if
// present
parseHeaders(md, method, classAnnotation);
}
return md;
}
Feign的封装当真是山路十八弯,父类子类各种相互调用,这点儿一点也不优雅
1 | protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) { |
- 解析ServiceAClient类上的
RequestMapping
注解
1 | protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) { |
- 这里其实就是将ServiceAClient类上面
RequestMapping
注解中解析出url
,然后将他发到Feign的RequestTemplate
组件中
1 | protected void processAnnotationOnMethod(MethodMetadata data, |
- 解析ServiceAClient中方法上的RequestMapping注解里各项属性
- 如果你没有指定方法类型,那么默认就是GET方法
- 解析method类型,放到RequestTemplate中的method中去
- 解析path,拼接成url,放到RequestTemplate中的URL中去
- 解析produces属性,将这些属性放到RequestTemplate中的header中的ACCEPT值里
- 解析consumes属性,将这些属性放到RequestTemplate中的header中的CONTENT_TYPE值的
- 解析headers属性,将这些属性放到RequestTemplate中的header键值对里去
1 | protected boolean processAnnotationsOnParameter(MethodMetadata data, |
解析ServiceAClient中方法上的入参
- 默认的参数处理器,这几个处理器都是spring-cloud-openfeign-core工程的
- 解析
@PathVariable
:PathVariableParameterProcessor
- 解析
@RequestParam
:RequestParamParameterProcessor
- 解析
@ReqeustHeader
:RequestHeaderParameterProcessor
- 解析
@QueryMap
:QueryMapParameterProcessor
- 解析
- 默认的参数处理器,这几个处理器都是spring-cloud-openfeign-core工程的
result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
create方法其实创建了一个
SynchronousMethodHandler
,将所有和serviceAClient中一个方法相关的东西都放了进去1
2
3
4
5
6
7
8
9
10
11
12final class SynchronousMethodHandler
class Factory
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404, closeAfterDecode, propagationPolicy);
}
总结
feign对FeignClient的创建过程,个人觉得封装的并不是很优雅,山路十八弯,各种父子类调用,很容易让源码阅读者绕晕。
按照惯例,笔者放出总结大图一张。