OpenFeign源码解析

本文深入解析Feign框架的核心功能及其实现原理,包括动态代理、请求拦截器、合同解析、客户端执行、编码器、解码器等关键组件的作用与工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

feign的核心功能就是通过接口去访问网络资源,猜一下里面也是用动态代理来实现的,就跟Mybatis用接口去访问数据库一样,今天我们就来看下源码的处理,核心就一个包:

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-core</artifactId>
</dependency>

先来看个demo:

//feign接口
public interface DemoClient {
    @RequestLine("GET /hello/{username}")
    public String hello(@Param("username") String username);
}
public class Demo {
    public static void main(String[] args) {
        DemoClient demoClient = Feign.builder()
                .target(DemoClient.class, "http://localhost:9000");
        //输出结果:com.sun.proxy.$Proxy3
        System.out.println(demoClient.getClass().getName());
        System.out.println(demoClient.hello("Joshua"));
    }
}

以上打印出的demoClient的类名是:com.sun.proxy.$Proxy3,很显然是jdk的动态代理生成的类名。

首先看下Feign#builder:

public static Builder builder() {
    //只是创建了一个Builder对象
    return new Builder();
}

我们看下Builder里面都有啥东西:

public static class Builder {
    private final List<RequestInterceptor> requestInterceptors =
        new ArrayList<RequestInterceptor>();
    private Logger.Level logLevel = Logger.Level.NONE;
    private Contract contract = new Contract.Default();
    private Client client = new Client.Default(null, null);
    private Retryer retryer = new Retryer.Default();
    private Logger logger = new NoOpLogger();
    private Encoder encoder = new Encoder.Default();
    private Decoder decoder = new Decoder.Default();
    private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
    private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
    private Options options = new Options();
    private InvocationHandlerFactory invocationHandlerFactory =
        new InvocationHandlerFactory.Default();
    private boolean decode404;
    private boolean closeAfterDecode = true;
    private ExceptionPropagationPolicy propagationPolicy = NONE;
    private boolean forceDecoding = false;
    private List<Capability> capabilities = new ArrayList<>();
}

我们挑几个重要的来说一下他们都是干嘛的。

(1)requestInterceptors

这个就是拦截器,可以在请求之前设置请求头、设置请求体、设置参数、设置url等等,类型是:RequestInterceptor:

public interface RequestInterceptor {
  void apply(RequestTemplate template);
}

RequestTemplate里面包含了最终用于创建Request的参数和配置,拦截器就是在往里面设置值。

(2)contract

private Contract contract = new Contract.Default();

默认是Contract.Default(),它主要是用来解析feign接口上的那些注解,比如:@QueryMap、@Param、@RequestLine、@Header、@Body、@HeaderMap等,以@Header为例:

public Default() {
      super.registerClassAnnotation(Headers.class, (header, data) -> {
        final String[] headersOnType = header.value();
        checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.",
            data.configKey());
        final Map<String, Collection<String>> headers = toMap(headersOnType);
        headers.putAll(data.template().headers());
        data.template().headers(null); // to clear
        data.template().headers(headers);
      });
      。。。。
}

做的事情就是把@Header(“name=value”)这里面的name=value取出来,重新设置到RequestTemplate里面。
(3)client

private Client client = new Client.Default(null, null);
public interface Client {
  Response execute(Request request, Options options) throws IOException;
}

client是真正去执行request,得到response的客户端,它的入参是一个Request,这个Request是用RequestTemplate构造出来的。我们看下default处理:

@Override
public Response execute(Request request, Options options) throws IOException {
  HttpURLConnection connection = convertAndSend(request, options);
  return convertResponse(connection, request);
}

convertAndSend()就是发请求,主要包括创建HttpURLConnection或者HttpsURLConnection,设置连接的各种超时时间(来自于Options),设置请求头、请求体,然后请求服务端。
convertResponse()就是解析响应,解析响应码、响应头、响应体。
client里面的东西其实没啥可看的。

(4)encoder

private Encoder encoder = new Encoder.Default();
public interface Encoder {
  void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
class Default implements Encoder {
    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) {
      if (bodyType == String.class) {
        template.body(object.toString());
      } else if (bodyType == byte[].class) {
        template.body((byte[]) object, null);
      } else if (object != null) {
        throw new EncodeException(
            format("%s is not a type supported by this encoder.", object.getClass()));
      }
    }
  }

encoder是用来编码请求的,默认能处理String和byte[]数组类型的参数,最终是存放在RequestTemplate里面。

(5)decoder

public interface Decoder {
  Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;
  public class Default extends StringDecoder {
    @Override
    public Object decode(Response response, Type type) throws IOException {
      if (response.status() == 404 || response.status() == 204)
        return Util.emptyValueOf(type);
      if (response.body() == null)
        return null;
      if (byte[].class.equals(type)) {
        return Util.toByteArray(response.body().asInputStream());
      }
      return super.decode(response, type);
    }
  }
}

decoder用来解码响应,默认可以返回字节数组和字符串。

(6)queryMapEncoder

private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();

它用来把@QueryMap注解标识的类的所有的字段都取出来放到url参数中。

(7)errorDecoder
异常处理

(8)invocationHandlerFactory


public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  interface MethodHandler {
    Object invoke(Object[] argv) throws Throwable;
  }

  static final class Default implements InvocationHandlerFactory {
    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }
}

就是在这里面创建的动态代理类。

当客户端调用Feign.builder()的时候,其实就是去设置builder里面的这些参数的值,最终需要调用target方法去生成代理类:

/**
  * apiType就是接口类,url就是服务端的地址
  */
public <T> T target(Class<T> apiType, String url) {
  return target(new HardCodedTarget<T>(apiType, url));
}

HardCodedTarget用来根据RequestTemplate构造出Request:

public interface Target<T> {
  public Request apply(RequestTemplate input);  
}
public static class HardCodedTarget<T> implements Target<T> {
  @Override
  public Request apply(RequestTemplate input) {
    if (input.url().indexOf("http") != 0) {
      input.target(url());
    }
    return input.request();
  }
}

继续看target方法:

public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

先看下build()方法:

public Feign build() {
  Client client = Capability.enrich(this.client, capabilities);
  Retryer retryer = Capability.enrich(this.retryer, capabilities);
  List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
      .map(ri -> Capability.enrich(ri, capabilities))
      .collect(Collectors.toList());
  Logger logger = Capability.enrich(this.logger, capabilities);
  Contract contract = Capability.enrich(this.contract, capabilities);
  Options options = Capability.enrich(this.options, capabilities);
  Encoder encoder = Capability.enrich(this.encoder, capabilities);
  Decoder decoder = Capability.enrich(this.decoder, capabilities);
  InvocationHandlerFactory invocationHandlerFactory =
      Capability.enrich(this.invocationHandlerFactory, capabilities);
  QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
  SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
      new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
          logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
  ParseHandlersByName handlersByName =
      new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
          errorDecoder, synchronousMethodHandlerFactory);
  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
public class ReflectiveFeign extends Feign {
  ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
        QueryMapEncoder queryMapEncoder) {
    this.targetToHandlersByName = targetToHandlersByName;
    this.factory = factory;
    this.queryMapEncoder = queryMapEncoder;
  }
}

就是构造Feign客户端对象ReflectiveFeign,并且添加默认值,注意下synchronousMethodHandlerFactory创建出来的MethodHandler的类型是SynchronousMethodHandler。handlersByName可以把feign接口解析成Map<String, MethodHandler>。

继续看newInstance(target):


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)) {
      //这个是处理default方法的
      DefaultMethodHandler handler = new DefaultMethodHandler(method);
      defaultMethodHandlers.add(handler);
      methodToHandler.put(method, handler);
    } else {
      //解析出来<Method,MethodHandler>
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
  }
  //ReflectiveFeign.FeignInvocationHandler,具体是如何调用的,看下它的处理
  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;
}

这里就很清晰了,返回的是动态代理类。

先看下nameToHandler是咋构造出来的:

public Map<String, MethodHandler> apply(Target target) {
  List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.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, target);
    } else if (md.bodyIndex() != null) {
      buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
    } else {
      buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
    }
    if (md.isIgnored()) {
      result.put(md.configKey(), args -> {
        throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
      });
    } else {
      result.put(md.configKey(),
      //SynchronousMethodHandler
      factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
    }
  }
  return result;
}

这里面主要就是使用contract去解析那些注解,然后把结果保存到 List里面,然后把list转化成map,其中map的key:


public static String configKey(Class targetType, Method method) {
  StringBuilder builder = new StringBuilder();
  builder.append(targetType.getSimpleName());
  builder.append('#').append(method.getName()).append('(');
  for (Type param : method.getGenericParameterTypes()) {
    param = Types.resolve(targetType, targetType, param);
    builder.append(Types.getRawType(param).getSimpleName()).append(',');
  }
  if (method.getParameterTypes().length > 0) {
    builder.deleteCharAt(builder.length() - 1);
  }
  return builder.append(')').toString();
}

说白了,就是把接口里面的每一个方法解析成一个key-value对,其中key是类名#方法名(参数类列表),value是SynchronousMethodHandler。
看下ReflectiveFeign.FeignInvocationHandler的invoke:


@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();
  }
  return dispatch.get(method).invoke(args);
}

equals、hashCode、toString直接就处理掉了,其他的方法往下走:

dispatch.get(method)拿到的是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;
    }
  }
}

这里还做做了重试,逻辑就不看了,看下executeAndDecode:

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
  //根据RequestTemplate构造出Request
  Request request = targetRequest(template);
  Response response;
  long start = System.nanoTime();
  try {  //执行request,得到response
    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
  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;
    }
  }
}

这个是核心的处理。

整个源码的扩展性还是非常好的,只要在builder里面替换掉相应的组件就可以了,后面我们会来看下springcloud里面是如何替换这里面的组件的。

欢迎扫码查看更多文章:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值