基于代理模式构建Feign.Builder
FeignClientFactoryBean
上一篇文章中,我们完成了Feign相关包以及组件扫描相关源码的阅读,这一节开始进行FeignClient相关组件的初始化,即通过代理模式生成相关的Bean.入口类为FeignClientFactoryBean
,本篇主要讲解如下内容
目录
- 完成Feign.Builder的构建
- 构建动态代理与LoadBalancerFeignClient
1.动态代理构建Feign.Builder
FeignClientFactoryBean
1.1.核心入口方法
和阅读其他源码的套路一样,我们先看@Override
相关的方法,找到如下
@Override
public Object getObject() throws Exception {
return getTarget();
}
下面我们来看这个核心方法的实现,代码片段如下
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
//因为没有在@FeignClient中指定url,因此会走下面的逻辑
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
this.url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
.....
}
FeignContext
在
FeignAutoConfiguration
中找到了FeignContext的初始化
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
FeignContext内部维护了一个map,key为服务名,value是对应的spring容器
初始化的时候分别将decoder,encoder,logger等组件全部放到该map中
1.2.构建Feign.Builder
feign(context);
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);//创建Slf4jLogger
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))//从FeignContext中获取ResponseEntityDecoder
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
//加载配置文件
configureFeign(context, builder);
return builder;
}
- 从
FeignContext
中获取到Feign.Builder
对应的组件- logger:Slf4jLogger,通过Factory创建
- Encoder:SpringEncoder
- Decoder
- Contract
- 读取配置文件,优先级从低到高
- 自定义配置类,若有,低
- default配置,中
- 指定服务的配置: 高
2.基于Feign.Builder构建FeignClient
这里创建的是
LoadBalancerFeignClient
HardCodedTarget
public static class HardCodedTarget<T> implements Target<T> {
private final Class<T> type;
private final String name;
private final String url;
}
在我们这里的HardCodedTarget
的属性分别是
- type: OrderClient,即对于的FeignClient
- name:
SERVICE-ORDER
- url:
http://SERVICE-ORDER
Client client = getOptional(context, Client.class);
protected <T> T getOptional(FeignContext context, Class<T> type) {
return context.getInstance(this.contextId, type);
}
从FeignContext中获取到LoadBalancerFeignClient,之后将该client设置到builder中
这一步我们完成了图中最下面这部分逻辑,即将服务名
,URL
,对应的client类型
构建一个Client->LoadBalancerFeignClient
,绑定到Feign.Builder上
2.创建动态代理对象的细节
HystrixTargeter.target()
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
}
这里的Builder是Feign.Builder
,因此会走上面的逻辑.最终通过feign.target(target)
走如下流程
public <T> T newInstance(Target<T> target) {
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.基于SpringMVCContract
解析方法的定义
第一行代码 Map
nameToHandler = targetToHandlersByName.apply(target); 该方法主要是将FeignClient中对应的多个请求接口转化为 MethodHandler
public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.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, target);
} else if (md.bodyIndex() != null) {
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;
}
}
其中MethodHandler
中包含的内容如上,
- key即为对应FeignClient中的请求接口,
- Client是和Ribbon整合的一个接口调用组件,即
LoadBalancerFeignClient
- 这里创建处理的
MethodHandler
是SynchronousMethodHandler