//Fegin的调用流程
/** 1.构造请求数据,将对象转为JSON
  *      RequestTemplate template = buildTemplateFromArgs,create(argv);
  * 2.发送请求进行执行《执行成功会解码响应数据):
  *      executeAndDecode(template);
  * 3.执行请求会有重试机制
  *      while(true){
  *          try{
  *              executeAndDecode(template);
  *          }catch(){
  *              try{retryer.continueOrpropagate(e);}catch()fthrow ex;)
  *              continue;
  *          }
  *
  *      }
  */


//位于ReflectiveFeign.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!"equals".equals(method.getName())) {
                //判断如果是hashCode或是toString方法就不需要远程调用
                if ("hashCode".equals(method.getName())) {
                    return this.hashCode();
                } else {
                    //这里是个三元运算符
                    return "toString".equals(method.getName()) ? 
                    this.toString() :
                    //这里才是真正的远程调用,接下来步入这个方法
                     ((MethodHandler)this.dispatch.get(method)).invoke(args);
                }
 ...
}

//SynchronousMethodHandler.class 代码演示
public Object invoke(Object[] argv) throws Throwable {
    //请求模板,包含请求路径、请求方式、请求体等信息
    //默认构造的模板是不包含请求头和请求参数的
    RequestTemplate template = this.buildTemplateFromArgs.create(argv);
    Options options = this.findOptions(argv);
    //这是一个重试器,Feign组件默认使用Ribbon的重试机制并增加了根据状态码判断重试机制。默认是关闭的。
    Retryer retryer = this.retryer.clone();

    while(true) {
        try {
            //真正的执行方法在这
            return this.executeAndDecode(template, options);
        } catch (RetryableException var9) {
            RetryableException e = var9;

            try {
                retryer.continueOrPropagate(e);
            } catch (RetryableException var8) {
                Throwable cause = var8.getCause();
                if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
                    throw cause;
                }

                throw var8;
            }

            if (this.logLevel != Level.NONE) {
                this.logger.logRetry(this.metadata.configKey(), this.logLevel);
            }
        }
    }
}