@Author:zxw
@Email:[email protected]
目录
- Feign源码分析(一) - 初探FeignFeign源码分析(二) - builder构建
通过前面的文章,已经分析清除了Feign代理类的生成流程。接下来就是看远程调用发起的流程Feign是如何实现的,代码还是跟之前一样,通过connect方法获取到代理对象后,直接调用Feign接口repo
@RequestLine("GET /api/v5/repos/{owner}/{repo}/stargazers?access_token=xxx&page=1&per_page=20") List2.源码分析repo(@Param("owner") String owner, @Param("repo") String repo); // ------ Gitee connect = Gitee.connect(); List star = connect.repo("xiaowei_zxw", "JSDX-JwSystem");
前面已经了解到Feign是使用的java接口代理的方式为我们生成了代理对象FeignInvocationHandler
2.1 FeignInvocationHandler对于java的接口代理,我们只需实现接口InvocationHandler即可
static class FeignInvocationHandler implements InvocationHandler
要发起代理调用首先我们得有远程地址的url,以及我们接口类的Class
private String url; private Classtype;
这些Feign则是封装在了HardCodedTarget类,那么得到FeignInvocationHandler的第一个元数据则是
private final Target target;
在生成代理对象之前,Feign为每个方法生成了一个MethodHandler封装了调用的基本信息,那么我们还需要一个Map映射已找到对应的MethodHandler,如下
private final Mapdispatch;
以上就是FeignInvocationHandler类中的两个字段,调用只需根据当前方法从Map中拿到对应的MethodHandler即可。
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);
通过Map拿到对象后,调用了MethodHandler的invoke方法。Feign中提供了MethodHandler的实现类SynchronousMethodHandler,接下来看SynchronousMethodHandler的invoke实现
2.2 SynchronousMethodHandler先来回顾一下MethodHandler的组成。Feign对接口的方法解析时会生成一个Methodmetadata对象
private final Methodmetadata metadata;
在先前分析Feign生成代理类时说过,Feign在解析方法注解后会生成一个Request的模板工厂类,通过该类可以获取RequestTemplate请求模板
private final RequestTemplate.Factory buildTemplateFromArgs;
对于远程调用的返回值则对应了一个解码器
private final Decoder decoder;
还需要调用的url等基本信息,上面已经提到这些Feign封装在了Target类中
private final Target> target;
在Feign中是以方法的维度发起调用,即每个MethodHandler中还封装了Client对象的基本信息
private final Client client;private final Retryer retryer;private final Options options;
这边对于MethodHandler的元数据大致就这些。
在回过来看看接口这个方法,在发起调用前的第一步肯定是组装我们的参数了,将param的值填充到对应的{}号
@RequestLine("GET /api/v5/repos/{owner}/{repo}/stargazers?access_token=xxx&page=1&per_page=20")Listrepo(@Param("owner") String owner, @Param("repo") String repo);
通过RequestTemplate.Factory工厂解析后就能得到一个RequestTemplate对象,该对象包含了http请求的模板信息,此时还不是最终请求的Request对象。
RequestTemplate template = buildTemplateFromArgs.create(argv);
得到了请求模板后,还需要经过请求拦截器调用后才能得到真正的Request对象
Request targetRequest(RequestTemplate template) { for (RequestInterceptor interceptor : requestInterceptors) { interceptor.apply(template); } return target.apply(template); }
最终得到了一个Request对象
Request request = targetRequest(template);
这时我们就可以使用client客户端发起我们的远程请求了。对于Client接口只提供了一个方法就是发起请求,这里也是一个扩展点,可以让我们定制化自己的client对象。
public interface Client { Response execute(Request request, Options options) throws IOException;}
如果我们不指定的话,Feign默认使用的就是java本身提供的网络访问对象HttpURLConnection
@Override public Response execute(Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection, request); }
至此远程调用就结束了,回头过看这块调用的完成逻辑
Response response; long start = System.nanoTime(); try { response = client.execute(request, options); // ensure the request is set. TODO: remove in Feign 12 response = response.toBuilder() .request(request) .requestTemplate(template) .build(); } catch (IOException e) { if (logLevel != Logger.Level.NONE) { logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start)); } throw errorExecuting(request, e); } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
在请求成功拿到Response返回值,这里我的解码器就可以出场对Response响应数据进行解码了。如果我们自定义了解码器,则Feign会使用我们的解码器,如果没有则使用Feign默认提供的解码器AsyncResponseHandler
if (decoder != null) return decoder.decode(response, metadata.returnType()); CompletableFuture
如果我们远程方法执行失败,Feign会捕获RetryableException异常进行重试,默认的重试次数为5。最后整个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) { try { retryer.continueOrPropagate(e); } catch (RetryableException th) { Throwable cause = th.getCause(); if (propagationPolicy == UNWRAP && cause != null) { throw cause; } else { throw th; } } if (logLevel != Logger.Level.NONE) { logger.logRetry(metadata.configKey(), logLevel); } continue; } } }3.总结
在本篇文章中,主要涉及到两个对象就是FeignInvocationHandler和SynchronousMethodHandler。一个是我们接口的代理类,一个则包含了远程请求调用的具体逻辑。
到这里整个Feign的源码就分析的差不多了,总得来说就是解析类上的注解,生成对应的请求信息,然后通过接口生成代理对象并执行远程调用方法。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)