所有文章

https://www.cnblogs.com/lay2017/p/11908715.html

正文

上一篇文章中,我们了解到了@FeignClient注解的接口被扫描到以后,会生成一个FeignClientFactoryBean的BeanDefinition。然后,spring将会通过调用FeignClientFactoryBean的getObject方法来获取@FeignClient注解的接口对应的代理对象。

生成proxy对象

本文,从FeignClientFactoryBean的getObject方法开始,看看代理对象的生成。跟进getObject方法

public Object getObject() throws Exception {
return getTarget();
}

继续跟进getTarget,该方法做了一些预处理。获取了一个上下文以及Feign的构造器,没有URL的情况下拼接了一个

FeignContext是在FeignAutoConfiguration被解析的时候成为Bean的

<T> T getTarget() {
// 获取一个上下文
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// feign用于构造代理对象,builder将会构建feign
Feign.Builder builder = feign(context); if (!StringUtils.hasText(this.url)) {
// 拼接URL地址,如:http://service-provider/
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));
}
// ... 省略
}

预处理之后,进入loadBalance方法

protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
// 获取执行http请求的客户端
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
// 选择获取代理对象的实现类,默认是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?");
}

获取代理对象的实现由Targeter的实现类处理,默认是HystrixTargeter

跟进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);
}
// ... return feign.target(target);
}

前面说过,Feign实现了构造代理对象的过程,所以这里将会回调feign的构造过程方法

跟进feign的target方法,build将会构造出Feign对象,而newInstance会返回代理对象

public <T> T target(Target<T> target) {
return build().newInstance(target);
} public Feign build() {
// ...
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

跟进newInstance方法,看看代理对象是如何被构建的

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);
// jdk的动态代理获取的代理对象
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}

代理对象的构建主要由三块内容

1、构建Method到MethodHandler的映射关系,后面调用代理的对象的时候将会根据Method找到MethodHandler然后调用MethodHandler的invoke方法,而MethodHandler将包含发起http请求的实现。

2、jdk动态代理需要提供InvocationHandler,这个大家比较熟悉了。而InvocationHandler将由InvocationHandlerFactory的create方法实现

3、通过Proxy.newProxyInstance方法,生成proxy对象。

这里我们看看factory.create方法生成InvocationHandler的实现吧

static final class Default implements InvocationHandlerFactory {

    @Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
// 这是一个内部类的实现
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}

调用proxy对象发起http请求

我们知道,jdk的动态代理将会调用FeignInvocationHandler的invoke方法。所以,我们看看FeignInvocationHandler是怎么调用Method的

private final Map<Method, MethodHandler> dispatch;

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ... return dispatch.get(method).invoke(args);
}

前面我们说到,构建proxy对象的时候会构建Method和MethodHandler的映射关系。而这里invoke代理对象的时候又会根据method来获取到MethodHandler,再调用其invoke方法。

MethodHandler的默认实现类是SynchronousMethodHandler,我们跟进它的invoke方法

public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
// ...
}
}
}

熟悉的代码来了,executeAndDecode将会负责发起http请求

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template); Response response; try {
// 执行http请求
response = client.execute(request, options);
} catch (IOException e) { } try {
//... // http请求成功
if (response.status() >= 200 && response.status() < 300) {
// 无需返回值
if (void.class == metadata.returnType()) {
return null;
} else {
// 解码结果
Object result = decode(response); return result;
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
// ...
} else {
// ...
}
} catch (IOException e) {
// ...
} finally {
// ...
}
}

总结

openFeign生成@FeignClient注解的接口的代理对象是从FeignClientFactoryBean的getObject方法开始的,生成proxy对象主要由ReflectiveFeign对象来实现。动态代理方法由jdk原生的动态代理支持。

调用proxy对象,其实就是发起http请求,请求结果将被解码并返回。

所以,正如Feign本身的意义一样,http远程调用被伪装成了本地调用一样简单的代理对象,对于使用者来说就是调用本地接口一样简单。

二、openfeign生成并调用客户端动态代理对象的更多相关文章

  1. Dubbo服务调用的动态代理和负载均衡

    Dubbo服务调用的动态代理及负载均衡源码解析请参见:http://manzhizhen.iteye.com/blog/2314514

  2. Mybatis-简单基于源码了解获取动态代理对象

    这是我们要测试的代码 OderDao就是我们要需要获取的对象. 首先我们根据传入的参数,进入SqlSessionFactoryBuilder 中的对应的build 方法,第一步创键XMLConfigB ...

  3. Java的三种代理模式(Spring动态代理对象)

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  4. spring获取当前动态代理对象

    由于spring中的aop拦截的是代理对象 当拦截的目标方法被本类的另一个方法调用时,会出现拦截失效 最佳实践: 例如拦截RegulationService的update(regulation)方法, ...

  5. C# 调用WebService的3种方式 :直接调用、根据wsdl生成webservice的.cs文件及生成dll调用、动态调用

    1.直接调用 已知webservice路径,则可以直接 添加服务引用--高级--添加web引用 直接输入webservice URL.这个比较常见也很简单 即有完整的webservice文件目录如下图 ...

  6. Android项目中JNI技术生成并调用.so动态库实现详解

    生成 jni方式有两种:一种是通过SWIG从C++代码生成过度的java代码:另一种是通过javah的方式从java代码自动生成过度的C++代码.两种方式下的步骤流程正好相反. 第一种方式:由于需要配 ...

  7. gsoap生成webservice调用客户端接口

    1.下载gsoap2.8 2.运行 wsdl2h.exe -o XXX.h XXX.wsdl wsdl文件可以是本地文件,也可以是服务器的wsdl,比如http://192.168.0.122:333 ...

  8. 生成并调用so动态库

    本文更新于2019-01-03. 生成库 头文件fn.h如下: #ifndef __FN_H__ #define __FN_H__ #ifdef __cplusplus extern "C& ...

  9. springdata find立即加载 get延迟加载 get返回的是一个动态代理对象 特点是 用的时候才会查询 否则不查询

随机推荐

  1. 线程池+同步io和异步io(浅谈)

    线程池+同步io和异步io(浅谈) 来自于知乎大佬的一个评论 我们的系统代码从同步方式+线程池改成异步化之后压测发现性能提高了一倍,不再有大量的空闲线程,但是CPU的消耗太大,几乎打满,后来改成协程化 ...

  2. 【缺少kubernetes权限】 namespaces "xxx" is forbidden: User "xxx" cannot xxx resource "xxx" in API group "xxx" in the namespace "xxx"

    需要添加权限,添加权限方式: https://github.com/argoproj/argo/issues/1068

  3. [Google] Help employee find the nearest gbike

    You are given a campus map with the Google buildings, roads and Google bikes. You have to help the e ...

  4. bootstrap-table:操作栏点击编辑按钮弹出模态框修改数据

    核心代码: columns: [ { checkbox:true //第一列显示复选框 }, ... { field: 'fail_num', title: '失败数' }, { field: 'op ...

  5. bootstrap:时间日期日历控件(datetimepicker)

    https://blog.csdn.net/qq_33368846/article/details/82223676 Bootstrap datetimepicker控件的使用 1.支持日期选择,格式 ...

  6. Windows Server 2012 安装 .NET 3.5 解决办法

    我遇到的每台Windows Server 2012都会遇到无法通过控制面板进行.net3.5安装的问题,在网上找了很多办法都不适合自己,最后研究出来一个办法就是 1.首先从镜像提取sxs文件放置到一个 ...

  7. springboot 通过docker 打包编译镜像

    添加plugin <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...

  8. 十分钟教会你使用安卓热修复框架AndFix

    腾讯最近开发出一个Tinker,阿里也有一个Dexposed框架,当然还有一个就是今天的主角热修复框架AndFix.接下来,我会从它的概念.原理.使用方法等为你详细介绍. 1.什么是AndFix? A ...

  9. 任务调度Quartz.Net之Windows Service

    这个应该是关于Quartz.Net使用的最后一篇文章了,之前的介绍都是基于Web的,这种实现任务调度的方式很少见,因为不管是MVC.WebApi还是WebService,它们都需要寄宿在IIS上运行, ...

  10. java 基础 HashMap 并发扩容问题

    存入的数据过多的时候,尤其是需要扩容的时候,在并发情况下是很容易出现问题. resize函数: void resize(int newCapacity) { Entry[] oldTable = ta ...