前言
在之前分析了@EnableFeignClients
及@FeignClient
是如何进行扫描及读取Feign 接口信息的,最后注解上的信息是被加载为BeanDefinition
放入了BeanDefinitionRegistry
中,接下来就是加载为Bean 对象了,依据下图(来自于百度)Feign 基本原理,分析下Feign 动态代理机制。
核心类
Target接口
Target
接口封装了feign
客户端接口的一些信息,并提供了将RequestTemplate
请求模板转化为实际请求的Request
的方法。
public interface Target<T> {
// 接口类型
Class<T> type();
// 客户端名称
String name();
//访问URL
String url();
// 将RequestTemplate 请求模板转化为实际请求的Request
Request apply(RequestTemplate var1);
}
Target
接口有两个实现类,EmptyTarget
和HardCodedTarget
。
EmptyTarget
意为空的目标,其没有URL属性。
HardCodedTarget
意为硬编码目标,Feign 中采用的是这个。
Client
Client
就是Feign 直接执行请求的接口,可以看到传入了Request
请求对象,及一个Options
对象(超时配置)。
public interface Client {
//
Response execute(Request var1, Options var2) throws IOException;
}
该接口的默认实现类为Default
,使用的是HttpURLConnection
,性能较低。
如果使用了OkHttp
,则会使用OkHttpClient
,一般实际开发,应当替换掉Default
。
FeignContext
FeignContext
继承了NamedContextFactory
,用于创建Spring 子容器,每个客户端都对应了唯一的一个FeignContext
。
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
// 只从子容器寻找bean,返回bean实例
@Nullable
public <T> T getInstanceWithoutAncestors(String name, Class<T> type) {
return BeanFactoryUtils.beanOfType(getContext(name), type);
}
// 只从子容器寻找bean,返回Map<bean名称,bean实例>
@Nullable
public <T> Map<String, T> getInstancesWithoutAncestors(String name, Class<T> type) {
return getContext(name).getBeansOfType(type);
}
}
Feign
Feign
是一个抽象类,提供了一个生成接口代理对象的核心方法。
public abstract <T> T newInstance(Target<T> var1);
还提供了一个构建者Builder
,用于创建Feign 接口实例
public Feign build() {
// Client 实例
Client client = (Client)Capability.enrich(this.client, this.capabilities);
// 重试器
Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);
// 拦截器
List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {
return (RequestInterceptor)Capability.enrich(ri, this.capabilities);
}).collect(Collectors.toList());
// 日志
Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);
// 模板契约
Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);
// 参数
Options options = (Options)Capability.enrich(this.options, this.capabilities);
// 编码器
Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);
// 解码器
Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);
InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
// 返回示例
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
ReflectiveFeign
实现了Feign
接口,重写了newInstance
方法,实际生产的Feign 客户端对象就是这个类。
ParseHandlersByName
是ReflectiveFeign
的静态内类,意思是通过名称解析处理器,它只有一个apply
方法:
public Map<String, MethodHandler> apply(Target target) {
// 方法元数据,Contract 会对传入的接口进行解析验证,看注解的使用是否符合规范,然后返回给我们接口上各种相应的元数据。
List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(target.type());
// 将方法名称,方法处理器放入一个Map 中,,MethodHandler则是包含此方法执行需要的各种信息
Map<String, MethodHandler> result = new LinkedHashMap();
Iterator var4 = metadata.iterator();
// 构建
while(var4.hasNext()) {
MethodMetadata md = (MethodMetadata)var4.next();
Object buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
} else {
buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.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(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
}
}
return result;
}
}
Targeter
Targeter
定义了target方法,它接收FeignClientFactoryBean
、Feign.Builde
r、FeignContext
、Target.HardCodedTarget
类型的参数。
interface Targeter {
<T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target);
}
最终生成的Bean 对象,是由该接口返回。
他有以下几个实现类:
DefaultTargeter
是其默认实现类,直接调用了Builder
中实际是Feign
子类实现的newInstance
方法创建实例。
public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {
return feign.target(target);
}
还有一个HystrixTargeter
,是可以创建基于Hystrix
的目标对象,默认使用的是这个。
流程分析
1. 获取对象
之前分析过,@FeignClient
上的所有信息,都会注册为FeignClientFactoryBean
类型的BeanDefinition
,学过Spring 的应该知道,FactoryBean
是一个工厂Bean,可以生成某一个类型Bean实例,其getObject()
方法会返回创建的Bean实例。
在Spring 容器进行实例化的时候,就会进入到FeignClientFactoryBean
的getObject()
方法,其实际调用的是本身的getTarget()
。首先可以看到FeignClientFactoryBean
中的相关属性:
2. 创建上下文、构建者
在getTarget()
中,是根据是否配置了URL
属性,创建不同的代理对象,没有URL,说明是需要从注册中心获取远程地址,使用的是loadBalance
方法加载动态的负载均衡。
<T> T getTarget() {
// 获取FeignContext ,每个Feign 客户端都对应一个context
FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
// 获取Feign 构建者
Builder builder = this.feign(context);
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
} else {
this.url = this.name;
}
// 拼接URL -=》http://order-service
this.url = this.url + this.cleanPath();
return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
} else {
// 判断是否配置了url 属性,配置了则直接根据URL创建代理对象
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + this.cleanPath();
Client client = (Client)this.getOptional(context, Client.class);
// 省略.....
return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
}
}
3. 创建Target
在loadBalance
方法中,首先会创建一个HardCodedTarget
对象,其中封装了Feign 客户端接口的类型、请求url、名称。
4. 生成代理对象
接着进入到loadBalance
方法,生成代理对象。
protected <T> T loadBalance(Builder builder, FeignContext context, HardCodedTarget<T> target) {
// 创建客户端
Client client = (Client)this.getOptional(context, Client.class);
if (client != null) {
//
builder.client(client);
// 生成代理对象
Targeter targeter = (Targeter)this.get(context, Targeter.class);
return targeter.target(this, builder, context, target);
} else {
throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
}
默认使用的是HystrixTargeter
:
public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {
// 没有使用hystric 直接创建Feign 的
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
} else {
// 省略
// 使用的hystric 会设置fallback
return fallbackFactory != Void.TYPE ? this.targetWithFallbackFactory(name, context, target, builder, fallbackFactory) : feign.target(target);
}
}
}
直接调用的是构建者Builder
的newInstance
方法为目标创建实例:
public <T> T target(Target<T> target) {
// 直接newInstance
return this.build().newInstance(target);
}
在newInstance
方法中,会为当前接口下每个方法创建方法处理器,然后再使用Proxy
创建动态代理对象。
public <T> T newInstance(Target<T> target) {
// 创建方法处理器,每个方法对应一个MethodHandler 实现类
Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
Method[] var5 = target.type().getMethods();
int var6 = var5.length;
// 循环方法Method 创建处理器
for(int var7 = 0; var7 < var6; ++var7) {
Method method = var5[var7];
if (method.getDeclaringClass() != Object.class) {
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 = this.factory.create(target, methodToHandler);
// 创建代理对象
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{
target.type()}, handler);
Iterator var12 = defaultMethodHandlers.iterator();
while(var12.hasNext()) {
DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
最终,该实例放入到了IOC中。