数据准备
@FeignClient注解 url 为空的
/**
* 定义依赖收款单服务的接口
*/
@FeignClient(value = "utopia-collection-center",contextId = "collectionCenterQueryAPI")
public interface CollectionQueryAPI {
//创建资金测收款单
@PostMapping(value = "/api/collection/create")
ResultDTO<Void> create(@RequestBody @Valid CollectionDTO collectionDTO);
//取消资金测收款单
@PostMapping(value = "/api/collection/cancel")
ResultDTO<Void> cancel(@Valid @RequestBody CancelCollectionDTO cancelCollectionDTO);
}
@FeignClient注解中 url属性不为空
@FeignClient(name = "member", url = "http://xxxx.member.com")
public interface MemberApi {
//批量查询人员信息
@GetMapping(value = "/api/v1/users")
List<UserRepresentationVo> batchQueryUserByUcId(@RequestParam("ids") List<String> ucIds);
}
feign底层原理介绍
feign扫描 @FeignClient 注解修饰的接口类,项目工程启动过程中,调用org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject生成 bean实例
org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
public Object getObject() throws Exception {
return getTarget();
}
org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
//@FeignClient(url = "http://xxx"): url值为空时的逻辑
//比如上面定义的:@FeignClient(value = "utopia-collection-center",contextId = "collectionCenterQueryAPI")
if (!StringUtils.hasText(this.url)) {
//省略代码....
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
//@FeignClient(url = "http://xxx"): url值不为空时的逻辑
//比如上面定义的:@FeignClient(name = "member", url = "http://xxxx.member.com")
//省略代码....
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
//1 因为有url所以客户端不需要走负载均衡
//2【注意】这里的client类型是:
//2.1 类型LoadBalancerFeignClient的成员属性delegate: (private final Client delegate)
//2.2 那getDelegate()返回的也就是实现了feign.Client接口的类了
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url));
}
context大概是:
org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
//【注意】这里的client类型是:类型LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
//Targeter有两个实现类
//1. org.springframework.cloud.openfeign.DefaultTargeter
//2. org.springframework.cloud.openfeign.HystrixTargeter
Targeter targeter = get(context, Targeter.class);
//
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
通过getTarget()和 loadBalance()两个方法可以看出来,@FeignClient注解的url属性在值是否为空的差一点是:
url值为空时:Client client获取的对象是LoadBalancerFeignClient类型及其子类型url值不为空时:Client client获取的对LoadBalancerFeignClient类型的属性delegate, 而delegate的类型是feign.Client类型
org.springframework.cloud.openfeign.HystrixTargeter#target
class HystrixTargeter implements Targeter {
@Override
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);
}
......
}
feign.Feign.Builder#target(feign.Target)
target 大概是这个样子
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
target()先是创建ReflectiveFeign对象,然后调用ReflectiveFeign.newInstance() 方法
feign.ReflectiveFeign#newInstance
public <T> T newInstance(Target<T> target) {
// apply()会解析 interface CollectionQueryAPI 接口定义的所有方法,并为每个方法创建一个map,
// 类似结构为:CollectionQueryAPI#create(CollectionDTO) -> new SynchronousMethodHandler
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)));
}
}
//创建new ReflectiveFeign.FeignInvocationHandler
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;
}
targetToHandlersByName.apply()方法 会解析 interface CollectionQueryAPI 接口定义的所有方法,并为每个方法创建一个map, 类似结构为:CollectionQueryAPI#create(CollectionDTO) -> new SynchronousMethodHandler
feign.ReflectiveFeign.ParseHandlersByName#apply
//feign.ReflectiveFeign.ParseHandlersByName#apply
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) {
......
//生成map: method -> new SynchronousMethodHandler
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
//feign.SynchronousMethodHandler.Factory#create
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.InvocationHandlerFactory.Default#create
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
到此为止,我们相当于讲了 使用@FeignClient注解的接口方法 扫描注册的过程,用一张图简单说一下: