Spring-Cloud Feign学习笔记

spring-cloud 里面的组件太多了,前段时间又接触到一个名字叫做Feign的,然后在CSDN看了很多大神写的这类文章,按照自己思路整理一下,就是为了加深下印象,萤火之光也不敢与日月争辉,但也希望对大家有帮助

1、我们先抛开spring-cloud 来说Fegin,我们之前做一个HTTP接口调用我们可以用JDK自带的HttpURLConnection,也可以用OKHttp3、RestTemplate等类库,来看我们用Fegin来实现一个

<!---这是案例的pom文件--->
<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<feign.version>10.3.0</feign.version>
</properties>
<dependencies>

	<!-- feign -->
	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-core</artifactId>
		<version>${feign.version}</version>
	</dependency>

	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-gson</artifactId>
		<version>${feign.version}</version>
	</dependency>

	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-okhttp</artifactId>
		<version>${feign.version}</version>
	</dependency>

	<dependency>
		<groupId>io.github.openfeign</groupId>
		<artifactId>feign-jackson</artifactId>
		<version>${feign.version}</version>
	</dependency>

</dependencies>

我们先来定义一个接口叫CsdnHub.java

public interface CsdnHub {

    <!--这个url其实是一个大神写feign的文章,推荐去看看写的很好-->
    @RequestLine("GET /Houson_c/article/details/84933840")
    String get();

}

然后我们直接用Feign来调用它,代码如下:

/**
     * main
     *
     * @param args
     */
    public static void main(String[] args) {
        //1、生成了一个CsdnHub接口代理类
        CsdnHub csdnHub = Feign.builder().decoder(new     Decoder.Default()).target(CsdnHub.class, "https://blog.csdn.net");
        String csdnResult = csdnHub.get();
        System.out.println(csdnResult);
    }

代码很简单,csdnResult结果就是 https://blog.csdn.net/Houson_c/article/details/84933840 这个网址的HTML内容,在这里我们要思考一下,它是怎么做到的呢??

2、我们直接转到target(CsdnHub.class, "https://blog.csdn.net") 这个方法定义,我们先看build()方法定义

public <T> T target(Class<T> apiType, String url) {
  return target(new HardCodedTarget<T>(apiType, url));
}

//从这个方法入手,这个方法build()方法和newInstance()
public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

public Feign build() {
  //1、创建一个methodhandler工厂类
  SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
	  new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
		  logLevel, decode404, closeAfterDecode, propagationPolicy);
  //2、用于解析接口配置的注解
  ParseHandlersByName handlersByName =
	  new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
		  errorDecoder, synchronousMethodHandlerFactory);
  //3、创建RefectiveFeign对象
  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

SynchronousMethodHandler.Factory代码贴出来,这个是SynchronousMethodHandler类中的内部类,代码如下

static class Factory {

    private final Client client;
    private final Retryer retryer;
    private final List<RequestInterceptor> requestInterceptors;
    private final Logger logger;
    private final Logger.Level logLevel;
    private final boolean decode404;
    private final boolean closeAfterDecode;
    private final ExceptionPropagationPolicy propagationPolicy;

    Factory(Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors,
        Logger logger, Logger.Level logLevel, boolean decode404, boolean closeAfterDecode,
        ExceptionPropagationPolicy propagationPolicy) {
      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;
      this.closeAfterDecode = closeAfterDecode;
      this.propagationPolicy = propagationPolicy;
    }

    public MethodHandler create(Target<?> target,
                                MethodMetadata md,
                                RequestTemplate.Factory buildTemplateFromArgs,
                                Options options,
                                Decoder decoder,
                                ErrorDecoder errorDecoder) {
      return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
          logLevel, md, buildTemplateFromArgs, options, decoder,
          errorDecoder, decode404, closeAfterDecode, propagationPolicy);
    }
  }

ParseHandlersByName 这个是解析接口注解的类,,这个是ReflectiveFeign类中的内部类,代码如下

static final class ParseHandlersByName {

    private final Contract contract;
    private final Options options;
    private final Encoder encoder;
    private final Decoder decoder;
    private final ErrorDecoder errorDecoder;
    private final QueryMapEncoder queryMapEncoder;
    private final SynchronousMethodHandler.Factory factory;

    ParseHandlersByName(
        Contract contract,
        Options options,
        Encoder encoder,
        Decoder decoder,
        QueryMapEncoder queryMapEncoder,
        ErrorDecoder errorDecoder,
        SynchronousMethodHandler.Factory factory) {
      this.contract = contract;
      this.options = options;
      this.factory = factory;
      this.errorDecoder = errorDecoder;
      this.queryMapEncoder = queryMapEncoder;
      this.encoder = checkNotNull(encoder, "encoder");
      this.decoder = checkNotNull(decoder, "decoder");
    }

    //apply
    public Map<String, MethodHandler> apply(Target key) {
      List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
        }
        result.put(md.configKey(),
            factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
      }
      return result;
    }
  }

3、接下来我们看newInstance()这个方法,看这个名字就大概能猜到这个方法会创建一个代理类,我们跟进去看看

public <T> T newInstance(Target<T> target) {
    //1、这个方法就是解析定义的那些接口,然后打包Map<String,MethodHandler> 返回
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    //2、遍历循环target定义的方法
    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)));
      }
    }
    //3、获取一个InvocationHandler实例,最后通过Proxy.newProxyInstance()代理对象
    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;
  }

总结:newInstance(Target<T> target) 这个方法主要做了两件事情

1-1:解释target接口中方法的注解信息,最后打包成Map<String,MethodHandler> 参数

1-2:通过target 和 methodToHandler参数生成得到一个InvocationHandler对象实例(这个对象实例是FeignInvocationHandler

)

1-3:通过target 和 invocationhandler对象实例 调用Proxy.newProxyInstance() 生成动态代理对象

1-4:最后调用代理对象时可以去看InvocationHandler对象实例的invoke()方法,也就是FeignInvocationHandler 类中的invoke()这个方法

private final Map<Method, MethodHandler> dispatch;

FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
  this.target = checkNotNull(target, "target");
  this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}

@Override
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();
  }
    
  //最后根据method 获取MethodHandler实例(也就是SynchronousMethodHandler对象中的invoke)
  return dispatch.get(method).invoke(args);
}

接下来可以去看SynchronousMethodHandler类中invoke()方法,就可以看到

@Override
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;
  }
}


Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      //1、这个里面就发起http请求了
      response = client.execute(request, options);
    } 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);
      }
      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 {
          Object result = decode(response);
          shouldClose = closeAfterDecode;
          return result;
        }
      } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
        Object result = decode(response);
        shouldClose = closeAfterDecode;
        return result;
      } 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上面流程清楚后,在看看大牛们画图这个流程图(我也是从别的大牛那边拷贝过来的),结合起来看效果更好

总结:Feign简化了各种Http请求操作,根据定义好的接口方法生成动态代理类HTTP目标方法 

发布了16 篇原创文章 · 获赞 10 · 访问量 7952

猜你喜欢

转载自blog.csdn.net/mnicsm/article/details/99828316