乐趣区

关于spring-cloud:SpringCloud升级之路20200x版27OpenFeign的生命周期创建代理

本系列代码地址:https://github.com/JoJoTec/sp…

接下来,咱们开始剖析 OpenFeign 的生命周期,联合 OpenFeign 自身的源代码。首先是从接口定义创立 OpenFeign 代理开始。咱们这里只关怀同步客户端,因为异步客户端目前还在实现中,并且在咱们的我的项目中,异步响应式的客户端不必 OpenFeign,而是用的官网的 WebClient

创立 OpenFeign 代理

创立 OpenFeign 代理,次要分为以下几步:

  1. 应用 Contract 解析接口的每一个办法,生成每一个办法的元数据列表:List<MethodMetadata> metadata
  2. 依据每一个 MethodMetadata,生成对应的申请模板工厂 RequestTemplate.Factory,用于生成前面的申请。同时,应用这个模板工厂以及其余配置生成对应的办法处理器 MethodHandler,对于同步的 OpenFeign,MethodHandler 实现为 SynchronousMethodHandler。将接口办法与 MethodHandler 一一对应建设映射,后果为 Map<Method, MethodHandler> methodToHandler。对于 Java 8 引入的 interface default 办法,须要用不同 MethodHandler,即 DefaultMethodHandler,因为这种办法不必代理,不必生成对应的 http 调用,其实现为间接调用对应的 default 办法代码。
  3. 应用 InvocationHandlerFactory 这个工厂,创立 InvocationHandler 用于代理调用。
  4. 调用 JDK 动静代理生成类办法应用 InvocationHandler 创立代理类。

创立 OpenFeign 代理,次要基于 JDK 的动静代理实现。咱们先举一个简略的例子,创立一个 JDK 动静代理,用来类比。

JDK 动静代理

应用 JDK 动静代理,须要如下几个步骤:

1. 编写接口以及对应的代理类 。咱们这里编写一个简略的接口和对应的实现类:

public interface TestService {void test();
}
public class TestServiceImpl implements TestService {
    @Override
    public void test() {System.out.println("TestServiceImpl#test is called");
    }
}

2. 创立代理类实现 java.lang.reflect.InvocationHandler,并且,在外围办法中,调用理论的对象,这里即咱们下面 TestService 的实现类 TestServiceImpl 的对象。

JDK 中有内置的动静代理 API,其外围是 java.lang.reflect.InvocationHandler。咱们先来创立一个简略的 InvocationHandler 实现类:

public class SimplePrintMethodInvocationHandler implements InvocationHandler {
    private final TestService testService;
    public SimplePrintMethodInvocationHandler(TestService testService) {this.testService = testService;}
    @Override
    public Object invoke(
            // 代理对象
            Object proxy,
            // 调用的办法
            Method method,
            // 应用的参数
            Object[] args)
            throws Throwable {System.out.println("Invoked method:" + method.getName());
        // 进行理论的调用
        return method.invoke(testService, args);
    }
}

3. 创立代理对象,并应用代理对象调用 。个别通过 Proxy 的静态方法去创立,例如:

// 首先,创立要代理的对象
TestServiceImpl testServiceImpl = new TestServiceImpl();
// 而后应用要代理的对象创立对应的 InvocationHandler
SimplePrintMethodInvocationHandler simplePrintMethodInvocationHandler = new SimplePrintMethodInvocationHandler(testServiceImpl);
// 创立代理类,因为一个类可能实现多个接口,所以这里返回的是 Object,用户依据本人须要强制转换成要用的接口
Object proxyInstance = Proxy.newProxyInstance(TestService.class.getClassLoader(),
        testServiceImpl.getClass().getInterfaces(),
        simplePrintMethodInvocationHandler
);
// 强制转换
TestService proxied = (TestService) proxyInstance;
// 应用代理对象进行调用
proxied.test();

这样,咱们就应用了 JDK 的内置动静代理机制实现了一个简略的动静代理。在 OpenFeign 的应用中,和咱们的示例有一点区别。首先,咱们只须要定义要代理的接口,不必定义实现类。因为所有的 OpenFeign 接口要做的事件其实都是 HTTP 调用,其信息能够主动从接口定义中生成,咱们能够应用对立的对象依据接口定义,承载 OpenFeign 接口定义的申请。在 OpenFeign 中,这个等同于实现对象的,就是依据接口生成的 MethodHandler,在同步的 OpenFeign 中,即 feign.SynchronousMethodHandler。之后,OpenFeign 创立的 InvocationHandler,其实就是将调用转发到对应的 SynchronousMethodHandler 的对应办法。

创立 OpenFeign 代理对象的流程详解

咱们应用后面的例子,来看一下创立代理的流程:

interface GitHub {
    /**
     * 定义 get 办法,包含门路参数,响应返回序列化类
     * @param owner
     * @param repository
     * @return
     */
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repository);

    /**
     * 响应体构造类
     */
    class Contributor {
        String login;
        int contributions;

        public Contributor() {}

        public String getLogin() {return login;}

        public void setLogin(String login) {this.login = login;}

        public int getContributions() {return contributions;}

        public void setContributions(int contributions) {this.contributions = contributions;}
    }
}

/**
 * 基于 FastJson 的反序列化解码器
 */
static class FastJsonDecoder implements Decoder {
    @Override
    public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
        // 读取 body
        byte[] body = response.body().asInputStream().readAllBytes();
        return JSON.parseObject(body, type);
    }
}
public static void main(String[] args) {
    // 创立 Feign 代理的 HTTP 调用接口实现
    GitHub github = Feign.builder()
                        // 指定解码器为 FastJsonDecoder
                        .decoder(new FastJsonDecoder())
                        // 指定代理类为 GitHub,基址为 https://api.github.com
                        .target(GitHub.class, "https://api.github.com");
    List<GitHub.Contributor> contributors = github.contributors("OpenFeign", "feign");
}

咱们这里关怀的其实就是创立 Feign 代理的 HTTP 调用接口实现这一步的外部流程。首先咱们来看 Feign 的 Builder 的构造,当咱们初始化一个 Feign 的 Builder 也就是调用 Feign.builder() 时,会创立如下组件(同时也阐明以下组件都是能够配置的,如果一些配置之前没有提到,则能够):

// 申请拦截器列表,默认为空
private final List<RequestInterceptor> requestInterceptors = new ArrayList();
// 日志级别,默认不打印任何日志
private Level logLevel = Level.NONE;
// 负责解析类元数据的 Contract,默认是反对 OpenFeign 内置注解的默认 Contract 
private Contract contract = new Contract.Default();
// 承载 HTTP 申请的 Client,默认是基于 Java HttpURLConnection 的 Default Client
private Client client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
// 重试器,默认也是 Default
private Retryer retryer = new feign.Retryer.Default();
// 默认的日志 Logger,默认不记录任何日志
private Logger logger = new NoOpLogger();
// 编码器解码器也是默认的
private Encoder encoder = new feign.codec.Encoder.Default();
private Decoder decoder = new feign.codec.Decoder.Default();
// 查问参数编码,这个咱们个别不会批改
private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
// 谬误编码器,默认为 Default
private ErrorDecoder errorDecoder = new feign.codec.ErrorDecoder.Default();
// 各种超时的 Options 走的默认配置
private Options options = new Options();
// 用来生成 InvocationHandler 的 Factory 也是默认的
private InvocationHandlerFactory invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
// 是否非凡解析 404 谬误,因为针对 404 咱们可能不想抛出异样,默认是 false
private boolean decode404 = false;
// 是否在解码后立即敞开 Response,默认为是
private boolean closeAfterDecode = true;
// 异样流传规定,默认是不流传
private ExceptionPropagationPolicy propagationPolicy = ExceptionPropagationPolicy.NONE;
// 是否强制解码,这个次要为了兼容异步 Feign 引入的配置,咱们间接疏忽,认为他就是 false 即可
private boolean forceDecoding = false;
private List<Capability> capabilities = new ArrayList();

咱们的代码中指定了指定解码器为 FastJsonDecoder,所以 Decoder 就是 FastJsonDecoder 了。最初通过 target(GitHub.class, "https://api.github.com"); 指定定代理类为 GitHub,基址为 https://api.github.com,这时候就会生成 Feign 代理类,其步骤是:

Feign

public <T> T target(Class<T> apiType, String url) {
  // 应用代理接口类型,以及基址创立 HardCodedTarget,他的意思其实就是硬编码的 Target
  return target(new HardCodedTarget<T>(apiType, url));
}

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

public Feign build() {
  // 将所有组件通过所有的 Capability,从这里咱们能够看出,咱们能够实现 Capability 接口来在创立 Feign 代理的时候动静批改组件
  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,用于生成 SynchronousMethodHandler,SynchronousMethodHandler 是理论承载 Feign 代理申请的实现类
  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);
  // 创立 ReflectiveFeign
  return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}

创立 ReflectiveFeign 之后,会调用其中的 newInstance 办法:

ReflectiveFeign

public <T> T newInstance(Target<T> target) {
    // 应用后面提到的 ParseHandlersByName 解析元数据并生成所有须要代理的办法的 MethodHandler,咱们这里剖析的是同步 Feign,所以是 SynchronousMethodHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    // 将办法与对应的 MethodHandler 一一对应
    for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() == Object.class) {
        // 对于 Object 的办法,间接跳过
        continue;
      } else if (Util.isDefault(method)) {
        // 如果是 java 8 中接口的默认办法,就应用 DefaultMethodHandler 解决
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    // 应用后面 Builder 中的 InvocationHandlerFactory 创立 InvocationHandler
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 应用 InvocationHandler 创立 Proxy
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);
    // 将代理与 DefaultMethodHandler 关联
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  } 

对于应用后面提到的 ParseHandlersByName 解析元数据并生成所有须要代理的办法的 MethodHandler 这一步,次要就波及到了应用 Contract 解析出办法的元数据,而后将这些元数据用对应的编码器绑定用于之后调用的编码:

ReflectiveFeign

public Map<String, MethodHandler> apply(Target target) {
      // 应用 Contract 解析出所有办法的元数据
      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) {
          // 有 body 的状况
          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 {
          // 应用 SynchronousMethodHandler 的 Factory 生成 SynchronousMethodHandler 
          result.put(md.configKey(),
              factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
        }
      }
      return result;
    }
  }

默认的 InvocationHandlerFactory 生成的 InvocationHandler 是 ReflectiveFeign.FeignInvocationHandler:

static final class Default implements InvocationHandlerFactory {
    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {return new ReflectiveFeign.FeignInvocationHandler(target, 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();
  }
  // 对于其余办法,调用对应的 SynchronousMethodHandler 进行解决
  return dispatch.get(method).invoke(args);
}

从这里咱们就能够看出,咱们生成的 Proxy,其实就是将申请代理到了 SynchronousMethodHandler 上。

咱们这一节具体介绍了 OpenFeign 创立代理的具体流程,能够看出,对于同步 Feign 生成的 Proxy,其实就是将接口 HTTP 申请定义的办法申请代理到了 SynchronousMethodHandler 上。下一节咱们会详细分析 SynchronousMethodHandler 做理论 HTTP 调用的流程,来搞清楚 Feign 所有组件是如何协调工作的。

微信搜寻“我的编程喵”关注公众号,每日一刷,轻松晋升技术,斩获各种 offer

退出移动版