【OpenFeign】 MethodHandler 方法处理器

Metadata

title: 【OpenFeign】 MethodHandler 方法处理器
date: 2023-01-02 19:37
tags:
  - 行动阶段/完成
  - 主题场景/组件
  - 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
  - 细化主题/Module/OpenFeign/组件
categories:
  - OpenFeign
keywords:
  - OpenFeign
description: 【OpenFeign】 MethodHandler 方法处理器

【OpenFeign】 MethodHandler 方法处理器

在之前的案例中,我们分析了注解扫描,加载 Feign 接口信息为FactoryBean,然后通过动态代理加载到 IOC 中,
在动态代理的newInstance过程中,会使用契约Contract创建方法元数据(MethodMetadata), 然后通过这些元数据为每个执行方法加载MethodHandler,接下来就具体分析下MethodHandler

MethodHandler接口非常简单,只有一个invoke方法,和 JDK 中的InvocationHandler#invoke类似,应该知道 JDK 中的动态代理,实现InvocationHandler后,代理对象执行时,实际执行的是其invoke方法,这里MethodHandler设计就是源于此,方法执行时,实际会调用MethodHandlerinvoke方法。

/**
   * Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a single method.
   */
  interface MethodHandler {

    Object invoke(Object[] argv) throws Throwable;
  }

MethodHandler有两个实现类,分别为 DefaultMethodHandler、SynchronousMethodHandler。

DefaultMethodHandlerMethodHandler的默认实现类,封装了一个MethodHandle(JDK 7 提供),MethodHandle类的实质是将某个具体的方法映射到MethodHandle上,通过MethodHandle直接调用该句柄所引用的底层方法,实际就是对可执行方法的引用。但是这个只能执行目标方法,并不具备远程通信的功能。

final class DefaultMethodHandler implements MethodHandler {
    private final MethodHandle unboundHandle;
    private MethodHandle handle;
    
    // 通过方法构造
    // 将方法绑定到MethodHandle 
    public DefaultMethodHandler(Method defaultMethod) {
        try {
            Class<?> declaringClass = defaultMethod.getDeclaringClass();
            Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP");
            field.setAccessible(true);
            Lookup lookup = (Lookup)field.get((Object)null);
            this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass);
        } catch (NoSuchFieldException var5) {
            throw new IllegalStateException(var5);
        } catch (IllegalAccessException var6) {
            throw new IllegalStateException(var6);
        }
    }
    // 将被代理的对象 绑定到MethodHandle 
    // bindTo方法必须在invoke方法之前调用
    public void bindTo(Object proxy) {
        if (this.handle != null) {
            throw new IllegalStateException("Attempted to rebind a default method handler that was already bound");
        } else {
            this.handle = this.unboundHandle.bindTo(proxy);
        }
    }
    
    // 代理对象执行时,调用目标方法
    public Object invoke(Object[] argv) throws Throwable {
        if (this.handle == null) {
            throw new IllegalStateException("Default method handler invoked before proxy has been bound.");
        } else {
            // 先通过genericMethodType方法得到MethodType,再通过MethodHandle的asType转换得到新的MethodHandle,
            // 最后通过新MethodHandle的invokeExact方法完成调用。
            return this.handle.invokeWithArguments(argv);
        }
    }
}

SynchronousMethodHandler就是同步方法处理器,可以从它的属性中看到很多涉及通信的相关类:

// 方法元数据
    private final MethodMetadata metadata;
    // 目标对象,默认为HardCodedTarget,
    private final Target<?> target;
    // 发送请求的客户端
    private final Client client;
    // 重试处理器
    private final Retryer retryer;
    // 请求拦截器
    private final List<RequestInterceptor> requestInterceptors;
    // 日志
    private final Logger logger;
    private final Level logLevel;
    // 请求模板构建工厂
    private final feign.RequestTemplate.Factory buildTemplateFromArgs;
    // 超时配置对象
    private final Options options;
    private final ExceptionPropagationPolicy propagationPolicy;
    // 解码器,解码响应体
    private final Decoder decoder;
    // 异步的响应处理器
    private final AsyncResponseHandler asyncResponseHandler;

可以看到其构造方法是私有的:

所以提供了一个内部工厂类来创建实例:

可以从它的invoke中看到,在目标对象执行时,会创建请求模板、加载配置、执行请求并进行解码,并完成重试机制。

1. 初始化

项目启动时,创建代理对象过程中,调用的是newInstance,在这里会为每个方法创建MethodHandler,然后将这些 Feign 接口请求方法及 default 方法都绑定到创建代理对象中。

public <T> T newInstance(Target<T> target) {
        // 每个对象方法,都创建一个MethodHandler,放入Map中
        // OrderFeign#post(Order) -> {SynchronousMethodHandler@6141} 
        Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        // 存放执行方法 MethodHandler
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
        // 存放接口默认default方法,不然这个接口有默认方法时,怎么执行
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        // 当前接口所有方法对象
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;
        // 循环
        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            // 接口中的default 方法放入defaultMethodHandlers 
            // Object 中的方法不处理
            // Feign 的方法,放入methodToHandler 
            if (method.getDeclaringClass() != Object.class) {
                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)));
                }
            }
        }
        // 创建InvocationHandler 
        InvocationHandler handler = this.factory.create(target, methodToHandler);
        // 创建代理对象
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        // 循环默认方法
        Iterator var12 = defaultMethodHandlers.iterator();
        // 将default  方法也绑定到代理对象中。
        while(var12.hasNext()) {
            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }

        return proxy;
    }

创建方法执行器的实际是this.targetToHandlersByName.apply(target),调用的是ParseHandlersByNameapply方法,会获取所有方法的元数据,然后构建BuildTemplateByResolvingArgs对象(用来创建请求模板),最后调用工厂类创建MethodHandler

public Map<String, MethodHandler> apply(Target target) {
                // 使用契约获取方法元数据(SpringMvcContract)
            List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(target.type());
            Map<String, MethodHandler> result = new LinkedHashMap();
            Iterator var4 = metadata.iterator();
            // 循环方法元数据列表
            while(var4.hasNext()) {
                MethodMetadata md = (MethodMetadata)var4.next();
                Object buildTemplate;
                // 没有请求体 且有表单参数,说明是普通参数请求
                // 请求体为x-www-form-urlencoded格式
                if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
                    buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
                } else if (md.bodyIndex() != null) {
                    // 存在请求体
                    buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
                } else {
                    //  没有请求体,也没有表单参数,走这个
                    buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
                }
                // 配置了忽略
                if (md.isIgnored()) {
                    result.put(md.configKey(), (args) -> {
                        throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
                    });
                } else {
                    // 创建方法执行器并Map 中,
                    result.put(md.configKey(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
                }
            }
            return result;
        }
    }

之后调用SynchronousMethodHandler的内部工厂类,创建对象。

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

最后创建的对象中,可以看到封装了该方法的元数据、目标对象等信息。

最终代理对象中,包含了这些方法处理器,是放在一个 Map 中,最后这些代理对象就被加载到了 IOC 中。

2. 方法执行

在 Feign 接口调用时,实际执行的是代理对象(JDK 为实现了 InvocationHandler 接口的对象),进入的是FeignInvocationHandler,如果不是 equals、hashCode、toString 等方法,则会执行this.dispatch.get(method)).invoke(args)进行方法执行。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 不是equals、hashCode、toString
            if (!"equals".equals(method.getName())) {
                if ("hashCode".equals(method.getName())) {
                    return this.hashCode();
                } else {
                    // 获取方法并执行
                    return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
                }
            } else {
                try {
                    Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                    return this.equals(otherHandler);
                } catch (IllegalArgumentException var5) {
                    return false;
                }
            }
        }

之前我们了解过,SynchronousMethodHandler会被放在动态代理对象的一个 Map 中,所以代理代理执行时,会从 Map 中根据当前的执行方法拿到对象的执行器。

(MethodHandler)this.dispatch.get(method))

直接直接调用对应执行器的invoke方法,传入的参数为当前方法的参数。

经过创建请求模板、配置、执行请求、解码,Feign 调用整个流程就结束了。