• Feign封装请求基本原理(方法调用)


    接上文“Feign封装请求基本原理(启动和注入)”,本文看一个Feign请求的过程。

    一、远程方法对应的MethodHandler创建

    在注入Feign代理对象的bean时,会给@FeignClient注解接口下所有符合条件的方法生成对应的MethodHandler,该操作是在ReflectiveFeign#newInstance()方法中。

    MethodHandler是远程方法请求的实际处理器,这里是MethodHandler的实现类SynchronousMethodHandler的对象。

    ReflectiveFeign#newInstance()方法

    // ReflectiveFeign#newInstance(Target<T> target)
    public <T> T newInstance(Target<T> target) {
    	// 该方法会生成远程方法对应的MethodHandler
    	// ParseHandlersByName#apply(Target target)
        Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    
        // 省略了其他代码
    }
    

    进入ReflectiveFeign$ParseHandlersByName#apply(Target target)方法

    // ReflectiveFeign$ParseHandlersByName#apply(Target target)
    // 本例中的target是 HardCodedTarget(type=FeignTest1Service, name=test1, url=http://hq.sinajs.cn)
    public Map<String, MethodHandler> apply(Target target) {
      // contract是SpringMvcContract对象,SpringMvcContract继承Contract$BaseContract类
      // 解析出每个方法对应的MethodMetadata
      List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      for (MethodMetadata md : metadata) {
      	// 实现了RequestTemplate$Factory接口,有一个create()用于创建请求模板RequestTemplate实例
        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 {
          // factory.create()创建SynchronousMethodHandler实例
          result.put(md.configKey(),
              factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
        }
      }
      return result;
    }
    

    关于请求路径、参数和头信息的初始化处理多是在SpringMvcContract类中。

    看解析接口方法MethodMetadata,进入SpringMvcContract#parseAndValidateMetadata(Class<?> targetType),即Contract$BaseContract#parseAndValidateMetadata(Class<?> targetType)

    // Contract$BaseContract#parseAndValidateMetadata(Class<?> targetType)
    public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
      // 不支持泛型 会抛异常
      checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s",
          targetType.getSimpleName());
      // 实现的接口不能超过1个 会抛异常
      checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s",
          targetType.getSimpleName());
      // 实现了一个接口,那被实现接口不能再实现的接口 会抛异常
      if (targetType.getInterfaces().length == 1) {
        checkState(targetType.getInterfaces()[0].getInterfaces().length == 0,
            "Only single-level inheritance supported: %s",
            targetType.getSimpleName());
      }
      Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
      // 遍历所有public方法
      for (Method method : targetType.getMethods()) {
      	// 合法的Feign方法不能是Object定义、静态方法和接口默认方法
        if (method.getDeclaringClass() == Object.class ||
            (method.getModifiers() & Modifier.STATIC) != 0 ||
            Util.isDefault(method)) {
          continue;
        }
        // 解析出方法对应的 MethodMetadata。如解析请求path、produces、consumes和headers等,包括对类和方法上的注解
        MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
        // 不支持重载方法 会抛异常
        checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s",
            metadata.configKey());
        result.put(metadata.configKey(), metadata);
      }
      return new ArrayList<>(result.values());
    }
    

    二、远程方法的调用

    1. 发起请求

    2. 代理对象调用处理方法

    Map<Method, MethodHandler> dispatch该对象缓存了远程方法请求的实际的处理逻辑,通过Method对象定位。

    FeignInvocationHandler#invoke(Object proxy, Method method, Object[] args)

    equals()、hashCode()和toString()方法不会被处理。

    dispatch.get(method).invoke(args),也就是进入SynchronousMethodHandler#invoke(Object[] argv)

    BuildTemplateByResolvingArgs#create(Object[] argv)

    SynchronousMethodHandler#targetRequest(RequestTemplate template),拦截器执行

    SynchronousMethodHandler#executeAndDecode(RequestTemplate template, Options options),发起请求和返回处理

    3. 执行结果

    原创 Doflamingo https://www.cnblogs.com/doflamingo
  • 相关阅读:
    SSM配置后可以访问静态html文件但无法访问其他后台接口的解决方案
    使用Idea部署SSM项目后,访问路径为url:8080/项目名_war_exploded的解决方案
    gym102302E_Chi's performance
    Linux命令之wc(Word Count)
    【贪心算法】最大整数
    贪心算法讲解及例题
    【贪心算法】均分纸牌
    【贪心算法】背包问题
    【基础题】字符串替换
    【基础题】对称排序
  • 原文地址:https://www.cnblogs.com/doflamingo/p/13412898.html
Copyright © 2020-2023  润新知