深入理解openFeign上篇:动态代生成代理对象

690 阅读2分钟

数据准备

@FeignClient注解 url 为空的

/**
* 定义依赖收款单服务的接口
*/
@FeignClient(value = "utopia-collection-center",contextId = "collectionCenterQueryAPI")
public interface CollectionQueryAPI {
	//创建资金测收款单
    @PostMapping(value = "/api/collection/create")
	ResultDTO<Void> create(@RequestBody @Valid CollectionDTO collectionDTO);

    //取消资金测收款单
	@PostMapping(value = "/api/collection/cancel")
	ResultDTO<Void> cancel(@Valid @RequestBody CancelCollectionDTO cancelCollectionDTO);
}

@FeignClient注解中 url属性不为空

@FeignClient(name = "member", url = "http://xxxx.member.com")
public interface MemberApi {
    //批量查询人员信息
	@GetMapping(value = "/api/v1/users")
	List<UserRepresentationVo> batchQueryUserByUcId(@RequestParam("ids")  List<String> ucIds);
}

feign底层原理介绍

feign扫描 @FeignClient 注解修饰的接口类,项目工程启动过程中,调用org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject生成 bean实例

org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject

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

org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget

<T> T getTarget() {
    FeignContext context = this.applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);

    //@FeignClient(url = "http://xxx"): url值为空时的逻辑
    //比如上面定义的:@FeignClient(value = "utopia-collection-center",contextId = "collectionCenterQueryAPI")
    if (!StringUtils.hasText(this.url)) {
        //省略代码....
        return (T) loadBalance(builder, context,
                new HardCodedTarget<>(this.type, this.name, this.url));
    }
    
    //@FeignClient(url = "http://xxx"): url值不为空时的逻辑
    //比如上面定义的:@FeignClient(name = "member", url = "http://xxxx.member.com")
    
    //省略代码....
    Client client = getOptional(context, Client.class);
    if (client != null) {
        if (client instanceof LoadBalancerFeignClient) {
            //1 因为有url所以客户端不需要走负载均衡
            //2【注意】这里的client类型是:
            	//2.1 类型LoadBalancerFeignClient的成员属性delegate: (private final Client delegate)
            	//2.2 那getDelegate()返回的也就是实现了feign.Client接口的类了
            client = ((LoadBalancerFeignClient) 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));
}

context大概是:

org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {

    //【注意】这里的client类型是:类型LoadBalancerFeignClient
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        //Targeter有两个实现类
        //1. org.springframework.cloud.openfeign.DefaultTargeter
        //2. org.springframework.cloud.openfeign.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?");
}

通过getTarget()loadBalance()两个方法可以看出来,@FeignClient注解的url属性在值是否为空的差一点是:

  • url值为空时:Client client获取的对象是LoadBalancerFeignClient类型及其子类型
  • url值不为空时:Client client获取的对 LoadBalancerFeignClient 类型的属性 delegate, 而 delegate的类型是 feign.Client类型

org.springframework.cloud.openfeign.HystrixTargeter#target

class HystrixTargeter implements Targeter {

@Override
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);
    }
    ......
}

feign.Feign.Builder#target(feign.Target)

target 大概是这个样子

public <T> T target(Target<T> target) {
    return build().newInstance(target);
}

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);
}

target()先是创建ReflectiveFeign对象,然后调用ReflectiveFeign.newInstance() 方法

feign.ReflectiveFeign#newInstance

public <T> T newInstance(Target<T> target) {
    // apply()会解析 interface CollectionQueryAPI 接口定义的所有方法,并为每个方法创建一个map, 
    // 类似结构为:CollectionQueryAPI#create(CollectionDTO) -> new SynchronousMethodHandler
    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)));
      }
    }
    //创建new ReflectiveFeign.FeignInvocationHandler
    InvocationHandler handler = factory.create(target, methodToHandler);

    //生成动态代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);
    
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}

targetToHandlersByName.apply()方法 会解析 interface CollectionQueryAPI 接口定义的所有方法,并为每个方法创建一个map, 类似结构为:CollectionQueryAPI#create(CollectionDTO) -> new SynchronousMethodHandler

feign.ReflectiveFeign.ParseHandlersByName#apply

//feign.ReflectiveFeign.ParseHandlersByName#apply

public Map<String, MethodHandler> apply(Target key) {
  List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
  Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
  for (MethodMetadata md : metadata) {
    ......
    //生成map: method -> new SynchronousMethodHandler
    result.put(md.configKey(),
        factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
  }
  return result;
}

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

feign.InvocationHandlerFactory.Default#create

@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
    return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}

到此为止,我们相当于讲了 使用@FeignClient注解的接口方法 扫描注册的过程,用一张图简单说一下: