天天看點

feign基礎源碼解析(初始化,調用過程)

執行個體調用過程

feign的核心主要分兩塊:

1,初始化

feign通過Feign.builder() 構造器建構傳遞初始化參數,如:發起http的工具,序列化工具,目标類,生産者位址等參數

然後解析目标類注解為每個方法生成對應的MethodHandler ,

然後使用jdk動态代理為目标類生成代理執行個體ReflectiveFeign.FeignInvocationHandler

2,調用過程

調用目标類執行個體方法實際調用的是代理對象 ReflectiveFeign.FeignInvocationHandler.invoke–>

同步調用方法處理器 SynchronousMethodHandler.invoke–> ApacheHttp裝飾者 ApacheHttpClient.execute

測試接口
public interface TestFeignClient {
    @GET
    @Path("test")
    String test(String str);

}
           
1,初始化過程,脫離spring自動裝配的情況下,使用TestFeignClient,建立目标類接口的代理類執行個體
TestFeignClient target = Feign.builder()
            .client(new ApacheHttpClient())
            .contract(new JAXRS2Contract())
            .decoder(new JacksonDecoder())
            .encoder(new JacksonEncoder())
            .target(TestFeignClient.class, "http://" + testClient);
            
    Feign.builder() 基礎建構器
    
    .client(new ApacheHttpClient()) 選擇具體發起http請求的工具
        由ApacheHttpClient 類實作 實作feign.Client 接口,
        核心實作方法execute(),通過解析轉換feign.Request 為 org.apache.http.HttpResponse
        然後通過apache.HttpClient 發起請求
        
    .contract(new JAXRS2Contract()) 使用 JAX-RS 2 注解辨別請求
    
    .decoder(new JacksonDecoder())  解碼器
    .encoder(new JacksonEncoder())  編碼器
    
    .target(TestFeignClient.class, "http://" + testClient); 使用jdk動态代理生成代理對象
        基礎建構之後建立預設對象 ReflectiveFeign
        1,new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
        2,調用 ReflectiveFeign.newInstance(target); 建立執行個體
        
          @Override
          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)));
              }
            }
            //生成代理處理器,調用的還是ReflectiveFeign 由他來的内部類FeignInvocationHandler實作jdk 
            // 動态代理類 InvocationHandler接口
            //      @Override
            //      public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
            //        return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
            //      }
            //
            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;
          }
           
2,接口調用過程
我們使用上一步擷取到的TestFeignClient執行個體直接調用 接口的方法TestFeignClient.test("aaa")
   當我們調用此執行個體的方法實際調用的是代理 ReflectiveFeign.FeignInvocationHandler.invoke 方法
           

ReflectiveFeign.FeignInvocationHandler.invoke

//目标類
        private final Target target;
        //解析目标類緩存方法對應請求
        private final Map<Method, MethodHandler> dispatch;
       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         //equals/hashCode/toString不發起請求
         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);
       }
           

SynchronousMethodHandler 繼承 MethodHandler 實作同步調用請求

@Override
         public Object invoke(Object[] argv) throws Throwable {
            //通過請求參數生成請求模闆
           RequestTemplate template = buildTemplateFromArgs.create(argv);
           //請求相關參數,timeout等
           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;
             }
           }
         }
      
      Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        //生成feign 請求對象
        Request request = targetRequest(template);
    
        if (logLevel != Logger.Level.NONE) {
          logger.logRequest(metadata.configKey(), logLevel, request);
        }
    
        Response response;
        long start = System.nanoTime();
        try {
          //調用 ApacheHttpClient feign 的裝飾者請求類
          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);
    
    
        if (decoder != null)
          return decoder.decode(response, metadata.returnType());
    
        CompletableFuture<Object> resultFuture = new CompletableFuture<>();
        asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
            metadata.returnType(),
            elapsedTime);
    
        try {
          if (!resultFuture.isDone())
            throw new IllegalStateException("Response handling not done");
    
          return resultFuture.join();
        } catch (CompletionException e) {
          Throwable cause = e.getCause();
          if (cause != null)
            throw cause;
          throw e;
        }
      }
           

ApacheHttpClient.execute 最終調用

private final HttpClient client;
      @Override
      public Response execute(Request request, Request.Options options) throws IOException {
        HttpUriRequest httpUriRequest;
        try {
          //feign request 轉換為ApacheHttpClient httpUriRequest
          httpUriRequest = toHttpUriRequest(request, options);
        } catch (URISyntaxException e) {
          throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);
        }
        //由 ApacheHttpClient 發起最終請求
        HttpResponse httpResponse = client.execute(httpUriRequest);
        return toFeignResponse(httpResponse, request);
      }