Netflix Feign - Spring Cloud 整合 Feign 源码(六)

194 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


FeignClientFactoryBean


获取Feign.Builder

FeignContext

Logger、Encoder、Decoder、Contract、Client & LoadBalancerFeignClient

Feign.Builder


获取Feign

Feign.Builder、Target & HardCodedTarget

Targeter & HystrixTargeter

Targeter#target() -> Feign.Builder#target -> builder()

MethodHandler & SynchronousMethodHandler

ParseHandersByName

Feign & ReflectiveFeign


获取Proxy

Feign#newInstance(target)

InvocationHandler & FeignInvocationHandler

Proxy proxy = Proxy.newInstance()

feign动态代理的请求处理机制

Feign动态代理进行请求的时候,根据请求的method对象,从Map<Method, MethodHandler> methodToHandler获取对应的SynchronousMethodHandler implements MethodHandler回调MethodHandler.invoke()

除非你在@FeignClient里配置一个url属性,指定你要访问的服务的url地址,才会走我们没看的一段源码逻辑,否则的话,直接是走loadBalance()方法来生成动态代理

Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);

对@FeignClient注解标注的ProductFacadeServiceFeign接口进行解析,解析里面的方法,然后为每个方法创建一个SynchronousMethodHandler,也就是说每个MethodHandler对应一个专门用来处理方法的请求调用。

target.type(),是ProductFacadeServiceFeign接口

JDK动态代理,可以认为动态生成了一个类,实现ProductFacadeServiceFeign接口的匿名类,基于ProductFacadeServiceFeign接口的匿名类创建了一个实例对象(动态代理对象 T proxy),对T proxy对象所有接口方法的调用,都会交给ReflectiveFeign.FeignInvocationHandler来处理。

MethodHandler创建以及Contract解析Spring Mvc注解

ParseHandlersByName targetToHandlersByName,包含Decoder、Encoder、Contract等feign的核心组件

List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());

key:HardCodedTarget(type=ProductFacadeServiceFeign, name=eureka-provider-ribbon-feign-api-impl, url= http://eureka-provider-ribbon-feign-api-impl)

key.type():HardCodedTarget类型

SpringMvcContract contract:解析接口的各种Spring Mvc注解,@RequestMappinng、@PathVariable、@RequestParam。默认情况下,feign不理解Spring Mvc的注解,Feign不知道如何处理。全都靠feign的Contract组件,来解析接口上的Spring Mvc的注解。

List<MethodMetadata> metadata:ProductFacadeServiceFeign接口中的所有方法。被SpringMvcContract组件解析,最后对每个方法都生成一个MethodMetadata,代表了这个方法的一些元数据。

MethodMetadata:

ProductFacadeServiceFeign#listProduct():

(1)configKey(方法的定义):ProductFacadeServiceFeign#listProduct()

(2)returnType(方法的返回类型):java.util.List<center.leon.eurekacommon.entity.ProductEntity>

(3)template(发送HTTP请求的模板):GET /product/facade HTTP/1.1

@RequestMapping(value = "/product/facade")
public interface ProductFacadeService {

    /**
     * @return
     */
    @GetMapping(value = "")
    List<ProductEntity> listProduct();
}

GET /product/facade HTTP/1.1:发送HTTP请求的模板,就是靠SpringMvcContract组件解析spring mvc的注解,构造出HTTP请求的模板

SpringMvcContract组件的工作原理

  1. 解析类上的@RequestMapping注解,获取uriTemplate的值【/product/facade】
  2. 解析方法上的@GetMapping注解,解析里面的value值,得到Http请求方法method是GET。uriTemplate的值【/product/facade】,method的值【GET】
  3. 硬编码HTTP协议,HTTP/1.1。template:GET /product/facade HTTP/1.1
  4. indexToName:解析的是@PathVariable注解。第一个占位符(index是0)要替换成方法入参里的id参数的值【0 -> id】。

ReflectiveFeign构建

public Feign build() {
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
        new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                             logLevel, decode404, closeAfterDecode, propagationPolicy);
    ParseHandlersByName handlersByName =
        new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
                                errorDecoder, synchronousMethodHandlerFactory);
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

在创建RefrectiveFeign的时候,就创建了一个SynchronousMethodHandler.Factory,作为SynchronousMethodHandler的工厂类。根据client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy构造相应的SynchronousMethodHandler。用来封装LoadBalancerFeignClient(负责发送请求)、Retryer(负责请求重试)、logger(请求拦截器)、logLevel(日志打印)。

对每个方法都创建了一个对应的SynchronousMethodHandler,同步方法处理器,这个SynchronousMethodHandler里面封装了发送请求需要的所有的组件,LoadBalancerFeignClient(负责发送请求)、Retryer(负责请求重试)、logger(请求拦截器)、logLevel(日志打印)。

\