zoukankan      html  css  js  c++  java
  • 二、openfeign生成并调用客户端动态代理对象

    所有文章

    https://www.cnblogs.com/lay2017/p/11908715.html

    正文

    上一篇文章中,我们了解到了@FeignClient注解的接口被扫描到以后,会生成一个FeignClientFactoryBean的BeanDefinition。然后,spring将会通过调用FeignClientFactoryBean的getObject方法来获取@FeignClient注解的接口对应的代理对象。

    生成proxy对象

    本文,从FeignClientFactoryBean的getObject方法开始,看看代理对象的生成。跟进getObject方法

    public Object getObject() throws Exception {
        return getTarget();
    }

    继续跟进getTarget,该方法做了一些预处理。获取了一个上下文以及Feign的构造器,没有URL的情况下拼接了一个

    FeignContext是在FeignAutoConfiguration被解析的时候成为Bean的

    <T> T getTarget() {
        // 获取一个上下文
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
        // feign用于构造代理对象,builder将会构建feign
        Feign.Builder builder = feign(context);
    
        if (!StringUtils.hasText(this.url)) {
            // 拼接URL地址,如:http://service-provider/
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            } else {
                this.url = this.name;
            }
            this.url += cleanPath();
            
            return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, this.url));
        }
        // ... 省略
    }

    预处理之后,进入loadBalance方法

    protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
        // 获取执行http请求的客户端
        Client client = getOptional(context, Client.class);
        if (client != null) {
            builder.client(client);
            // 选择获取代理对象的实现类,默认是HystrixTargeter
            Targeter targeter = get(context, Targeter.class);
            return targeter.target(this, builder, context, target);
        }
    
        throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
    }

    获取代理对象的实现由Targeter的实现类处理,默认是HystrixTargeter

    跟进HystrixTargeter的target方法

    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) {
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
            return feign.target(target);
        }
        // ...
    
        return feign.target(target);
    }

    前面说过,Feign实现了构造代理对象的过程,所以这里将会回调feign的构造过程方法

    跟进feign的target方法,build将会构造出Feign对象,而newInstance会返回代理对象

    public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }
    
    public Feign build() {
      // ...
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }

    跟进newInstance方法,看看代理对象是如何被构建的

    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)) {
            DefaultMethodHandler handler = new DefaultMethodHandler(method);
            defaultMethodHandlers.add(handler);
            methodToHandler.put(method, handler);
          } else {
            methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
          }
        }
        InvocationHandler handler = factory.create(target, methodToHandler);
        // jdk的动态代理获取的代理对象
        T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler);
    
        for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
          defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
    }

    代理对象的构建主要由三块内容

    1、构建Method到MethodHandler的映射关系,后面调用代理的对象的时候将会根据Method找到MethodHandler然后调用MethodHandler的invoke方法,而MethodHandler将包含发起http请求的实现。

    2、jdk动态代理需要提供InvocationHandler,这个大家比较熟悉了。而InvocationHandler将由InvocationHandlerFactory的create方法实现

    3、通过Proxy.newProxyInstance方法,生成proxy对象。

    这里我们看看factory.create方法生成InvocationHandler的实现吧

    static final class Default implements InvocationHandlerFactory {
    
        @Override
        public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
          // 这是一个内部类的实现
          return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
        }
      }

    调用proxy对象发起http请求

    我们知道,jdk的动态代理将会调用FeignInvocationHandler的invoke方法。所以,我们看看FeignInvocationHandler是怎么调用Method的

    private final Map<Method, MethodHandler> dispatch;
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // ...
    
      return dispatch.get(method).invoke(args);
    }

    前面我们说到,构建proxy对象的时候会构建Method和MethodHandler的映射关系。而这里invoke代理对象的时候又会根据method来获取到MethodHandler,再调用其invoke方法。

    MethodHandler的默认实现类是SynchronousMethodHandler,我们跟进它的invoke方法

    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) {
            // ...
          }
        }
      }

    熟悉的代码来了,executeAndDecode将会负责发起http请求

    Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        Request request = targetRequest(template);
    
        Response response;
    
        try {
             // 执行http请求
          response = client.execute(request, options);
        } catch (IOException e) {
          
        }
    
        try {
          //...
    
          // http请求成功
          if (response.status() >= 200 && response.status() < 300) {
              // 无需返回值
            if (void.class == metadata.returnType()) {
              return null;
            } else {
              // 解码结果
              Object result = decode(response);
    
              return result;
            }
          } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
            // ...
          } else {
            // ...
          }
        } catch (IOException e) {
          // ...
        } finally {
          // ...
        }
      }

    总结

    openFeign生成@FeignClient注解的接口的代理对象是从FeignClientFactoryBean的getObject方法开始的,生成proxy对象主要由ReflectiveFeign对象来实现。动态代理方法由jdk原生的动态代理支持。

    调用proxy对象,其实就是发起http请求,请求结果将被解码并返回。

    所以,正如Feign本身的意义一样,http远程调用被伪装成了本地调用一样简单的代理对象,对于使用者来说就是调用本地接口一样简单。

  • 相关阅读:
    SQL Server 内存中OLTP内部机制概述(四)
    SQL Server 内存中OLTP内部机制概述(三)
    SQL Server 内存中OLTP内部机制概述(二)
    SQL Server 内存中OLTP内部机制概述(一)
    用例图——远程网络教学系统
    jsp动作标签
    JavaBean
    JSP的内置对象——session
    JSP内置对象——response
    设计一个简单的网上答题及评测系统
  • 原文地址:https://www.cnblogs.com/lay2017/p/11946324.html
Copyright © 2011-2022 走看看