Fegin源码解析

OpenFegin源码解析图

① BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
  Spring Cloud FengnClient实际上是利用Spring的代理工厂来生成代理类,所以在这里地方才会把所有的FeignClient的BeanDefinition设置为FeignClientFactoryBean类型,而
  FeignClientFactoryBean继承自FactoryBean,它是一个工厂Bean。在Spring中,FactoryBean是一个工厂Bean,用来创建代理Bean。工厂 Bean 是一种特殊的 Bean, 对于 Bean 的消费者来说, 
  他逻辑上是感知不到这个 Bean 是普通的 Bean 还是工厂 Bean, 只是按照正常的获取 Bean 方式去调用, 但工厂bean 最后返回的实例不是工厂Bean 本身, 而是执行工厂 Bean 的 getObject 逻辑返回的示例。
② ## ReflectiveFeign.newInstance  
   Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
   Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
   List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

FeignClientsRegistrar

  • registerDefaultConfiguration 方法内部从 SpringBoot 启动类上检查是否有@EnableFeignClients, 有该注解的话, 则完成 Feign 框架相关的一些配置内容注册
  • registerFeignClients 方法内部从 classpath 中, 扫描获得 @FeignClient 修饰的类, 将类的内容解析为 BeanDefinition , 最终通过调用 Spring 框架中的BeanDefinitionReaderUtils.resgisterBeanDefinition
    将解析处理过的 FeignClientBeanDeifinition 添加到 spring 容器中.
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
      //注册@EnableFeignClients中定义defaultConfiguration属性下的类,包装成FeignClientSpecification,注册到Spring容器。
      //在@FeignClient中有一个属性:configuration,这个属性是表示各个FeignClient自定义的配置类,后面也会通过调用registerClientConfiguration方法来注册成FeignClientSpecification到容器。
      //所以,这里可以完全理解在@EnableFeignClients中配置的是做为兜底的配置,在各个@FeignClient配置的就是自定义的情况。
      registerDefaultConfiguration(metadata, registry);
      registerFeignClients(metadata, registry);
}

FeignClientFactoryBean.getObject

getObject调用的是getTarget方法,它从applicationContext取出FeignContext,FeignContext继承了NamedContextFactory,它是用来来统一维护feign中各个feign客户端相互隔离的上下文。

// FeignContext注册到容器是在FeignAutoConfiguration上完成的
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
      FeignContext context = new FeignContext();
      context.setConfigurations(this.configurations);
      return context;
}
//在初始化FeignContext时,会把configurations在容器中放入FeignContext中。configurations的来源就是在前面registerFeignClients方法中将@FeignClient的配置configuration。

Target.target()

//配置完Feign.Builder之后,再判断是否需要LoadBalance,如果需要,则通过LoadBalance的方法来设置。实际上他们最终调用的是Target.target()方法。

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

<T> T getTarget() {
	FeignContext context = this.applicationContext.getBean(FeignContext.class);
	Feign.Builder builder = feign(context);
      //如果url为空,则走负载均衡,生成有负载均衡功能的代理类
	if (!StringUtils.hasText(this.url)) {
		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));
	}
        //如果指定了url,则生成默认的代理类
	if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
		this.url = "http://" + this.url;
	}
	String url = this.url + cleanPath();
	Client client = getOptional(context, Client.class);
	if (client != null) {
		if (client instanceof LoadBalancerFeignClient) {
			// not load balancing because we have a url,
			// but ribbon is on the classpath, so unwrap
			client = ((LoadBalancerFeignClient) client).getDelegate();
		}
		if (client instanceof FeignBlockingLoadBalancerClient) {
			// not load balancing because we have a url,
			// but Spring Cloud LoadBalancer is on the classpath, so unwrap
			client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
		}
		builder.client(client);
	}
        //生成默认代理类
	Targeter targeter = get(context, Targeter.class);
	return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url));
}

loadBalance

生成具备负载均衡能力的feign客户端,为feign客户端构建起绑定负载均衡客户端Client client = (Client)this.getOptional(context, Client.class);
从上下文中获取一个Client,默认是LoadBalancerFeignClient。它是在FeignRibbonClientAutoConfiguration这个自动装配类中,通过Import实现的

@Import({ HttpClientFeignLoadBalancedConfiguration.class,
      OkHttpFeignLoadBalancedConfiguration.class,
      DefaultFeignLoadBalancedConfiguration.class })
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
	Client client = getOptional(context, Client.class);
	if (client != null) {
		builder.client(client);
		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?");
}

targetToHandlersByName.apply(target)

根据Contract协议规则,解析接口类的注解信息,解析成内部表现:
targetToHandlersByName.apply(target);会解析接口方法上的注解,从而解析出方法粒度的特定的配置信息,然后生产一个SynchronousMethodHandler 然后需要维护一个<method,MethodHandler>的map,放入InvocationHandler的实现FeignInvocationHandler中。

public Map<String, MethodHandler> apply(Target target) {
      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) {
          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 {
          result.put(md.configKey(),
              factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
        }
      }
      return result;
    }
  }

SpringMvcContract

当前Spring Cloud 微服务解决方案中,为了降低学习成本,采用了Spring MVC的部分注解来完成 请求协议解析,也就是说 ,写客户端请求接口和像写服务端代码一样:客户端和服务端可以通过SDK的方式进行约定,客户端只需要引入服务端发布的SDK API,就可以使用面向接口的编码方式对接服务。

OpenFeign调用过程

OpenFeign最终返回的是一个#ReflectiveFeign.FeignInvocationHandler的对象。
那么当客户端发起请求时,会进入到FeignInvocationHandler.invoke方法中,它是一个动态代理的实现。

而接着,在invoke方法中,会调用 this.dispatch.get(method)).invoke(args) 。this.dispatch.get(method) 会返回一个SynchronousMethodHandler,进行拦截处理。
这个方法会根据参数生成完成的RequestTemplate对象,这个对象是Http请求的模版,代码如下。

@Override
  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) {
        try {
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

动态生成Request

executeAndDecode

我们已经将restTemplate拼装完成,上面的代码中有一个 executeAndDecode() 方法,该方法通过RequestTemplate生成Request请求对象,然后利用Http Client获取response,来获取响应信息。

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    //转化为http请求报文
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      //发起远程通信
      response = client.execute(request, options);
      // 返回结果
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

    boolean shouldClose = true;
    try {
      if (logLevel != Logger.Level.NONE) {
        response =
            logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
      }
      if (Response.class == metadata.returnType()) {
        if (response.body() == null) {
          return response;
        }
        if (response.body().length() == null ||
            response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
          shouldClose = false;
          return response;
        }
        // Ensure the response body is disconnected
        byte[] bodyData = Util.toByteArray(response.body().asInputStream());
        return response.toBuilder().body(bodyData).build();
      }
      if (response.status() >= 200 && response.status() < 300) {
        if (void.class == metadata.returnType()) {
          return null;
        } else {
          Object result = decode(response);
          shouldClose = closeAfterDecode;
          return result;
        }
      } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
        Object result = decode(response);
        shouldClose = closeAfterDecode;
        return result;
      } else {
        throw errorDecoder.decode(metadata.configKey(), response);
      }
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
      }
      throw errorReading(request, response, e);
    } finally {
      if (shouldClose) {
        ensureClosed(response.body());
      }
    }
  }

Client.execute

默认采用JDK的 HttpURLConnection 发起远程调用

 @Override
    public Response execute(Request request, Options options) throws IOException {
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
    }

    Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
      int status = connection.getResponseCode();
      String reason = connection.getResponseMessage();

      if (status < 0) {
        throw new IOException(format("Invalid status(%s) executing %s %s", status,
            connection.getRequestMethod(), connection.getURL()));
      }

      Map<String, Collection<String>> headers = new LinkedHashMap<>();
      for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {
        // response message
        if (field.getKey() != null) {
          headers.put(field.getKey(), field.getValue());
        }
      }

      Integer length = connection.getContentLength();
      if (length == -1) {
        length = null;
      }
      InputStream stream;
      if (status >= 400) {
        stream = connection.getErrorStream();
      } else {
        stream = connection.getInputStream();
      }
      return Response.builder()
          .status(status)
          .reason(reason)
          .headers(headers)
          .request(request)
          .body(stream, length)
          .build();
    }
posted @ 2020-12-07 11:41  snail灬  阅读(121)  评论(0编辑  收藏  举报