美文网首页
源码解析: Feign MethodHandler

源码解析: Feign MethodHandler

作者: lazyguy | 来源:发表于2018-01-03 16:44 被阅读0次

    作用

    MethodHandler从字面意思理解这是个方法管理器

     /**
       * Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a
       * single method.
       */
      interface MethodHandler {
    
        Object invoke(Object[] argv) throws Throwable;
      }
    

    如注释所言,如果熟悉java.lang.reflect.InvocationHandler#invoke。大概就能明白。这是为了执行一个方法而预备的组件。
    那么里面就应该包含了我们通过执行HTTP请求从而完成一个预定义的方法的逻辑。

    SynchronousMethodHandler

    这个类是Feign对MethodHandler的默认实现。参数太多,多以通过Factory工厂类去创建。

        Factory(Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors,
                Logger logger, Logger.Level logLevel, boolean decode404) {
          this.client = checkNotNull(client, "client");
          this.retryer = checkNotNull(retryer, "retryer");
          this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors");
          this.logger = checkNotNull(logger, "logger");
          this.logLevel = checkNotNull(logLevel, "logLevel");
          this.decode404 = decode404;
        }
    

    这里需要为其提供6个属性,既图中红色部分的组件。大致能猜出它们的作用。

    1. Client
      代表的是真正去执行HTTP请求的组件。他可以是OkHttpClient,也可以是Apache的HttpClient。草草观察Feign的默认实现Client.Default类可以看出其逻辑就是通过传入的Request相关参数,打开一个HttpURLConnection,执行完毕后,转换结果为Response对象,关闭连接。
    2. Retryer
      当我们的http请求通过client执行后,如果出现RetryableException。
      由Retryer这个组件负责决定是否按规定逻辑重新执行,或者抛出异常,中断执行。
      因为每个客户端发起的HTTP请求执行的情况各个不同,所以我们必须为每一个的http请求单独生成一个Retryer的实例,保存相关信息。这里使用了Clone,实现原型模式。
    3. RequestInterceptor
      为每个请求执行前加入拦截器的逻辑
    4. Logger
      Logger虚类自己实现了如下几个方法,你可以从名字和代码知道他们大概是在什么时候会被调用,用来记录日志。
      logRequest
      logRetry
      logAndRebufferResponse
      logIOException
      另外留下了一个log方法给子类自己去实现记录日志的场景逻辑。
    5. decode404
      这是个布尔值,是否反编码404?如果没看使用说明你也能猜到对于执行HTTP请求后得到404的情况,Feign提供了特殊的选项,其实就是就是抛出异常还是返回null的区别。

    总结以上几个组件的作用。他们在一起的作用是“执行HTTP请求”。所以我们传给SynchronousMethodHandler.Factory的改造参数是告诉怎么去“执行HTTP请求”。

    invoke方法

    这个invoke方法就是我们真正调用某个接口的某个方法的时候,去执行的完整逻辑,只不过最后这个逻辑会被嵌入动态代理生成的类中而已。准备这个方法所需要的组件简直就是山路十八弯,但是这些组件最后被使用,就这么点 代码……

    @Override
      public Object invoke(Object[] argv) throws Throwable {
        //这个RequestTemplate可以在每次调用时创建一个Request。
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        //Retryer对于每次调用都需要一个单独的重试计数器。
        Retryer retryer = this.retryer.clone();
        while (true) {
          try {
            return executeAndDecode(template);
          } catch (RetryableException e) {
            retryer.continueOrPropagate(e);
            if (logLevel != Logger.Level.NONE) {
              logger.logRetry(metadata.configKey(), logLevel);
            }
            continue;
          }
        }
      }
    
    Object executeAndDecode(RequestTemplate template) throws Throwable {
        Request request = targetRequest(template);
    
        if (logLevel != Logger.Level.NONE) {
          logger.logRequest(metadata.configKey(), logLevel, request);
        }
    
        Response response;
        long start = System.nanoTime();
        try {
          response = client.execute(request, options);
          // ensure the request is set. TODO: remove in Feign 10
          response.toBuilder().request(request).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);
    
        boolean shouldClose = true;
        try {
          if (logLevel != Logger.Level.NONE) {
            response =
                logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
            // ensure the request is set. TODO: remove in Feign 10
            response.toBuilder().request(request).build();
          }
          if (Response.class == metadata.returnType()) {
            if (response.body() == null) {
              return response;
            }
            if (response.body().length() == null ||
                    response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
              shouldClose = false;
              return response;
            }
            // Ensure the response body is disconnected
            byte[] bodyData = Util.toByteArray(response.body().asInputStream());
            return response.toBuilder().body(bodyData).build();
          }
          if (response.status() >= 200 && response.status() < 300) {
            if (void.class == metadata.returnType()) {
              return null;
            } else {
              return decode(response);
            }
          } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
            return decode(response);
          } else {
            throw errorDecoder.decode(metadata.configKey(), response);
          }
        } catch (IOException e) {
          if (logLevel != Logger.Level.NONE) {
            logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
          }
          throw errorReading(request, response, e);
        } finally {
          if (shouldClose) {
            ensureClosed(response.body());
          }
        }
      }
    

    相关文章

      网友评论

          本文标题:源码解析: Feign MethodHandler

          本文链接:https://www.haomeiwen.com/subject/tgghnxtx.html