Spring Cloud Open Feign系列【17】MethodHandler方法处理器源码分析

文章目录

  • 前言
  • MethodHandler接口
  • 流程分析
      1. 初始化
    1. 方法执行

前言

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

MethodHandler接口

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

 /**
   * 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 调用整个流程就结束了。
 

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: