Feign源码深度刨析-(5)核心组件FeignClientFactoryBean(下)

“不积跬步,无以至千里。”

回顾一下,前面说了,把Feign.Builder、context、HardCodedTarget 一起传入了一个loadBalance()方法里面,最后给 return 了,这个 return 的就是feign client的代理对象,然后注入给Controller的属性

这篇文章来着重分析一下 loadBalance() 方法

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

Client client = getOptional(context, Client.class);

上来从context(spring容器)中拿出来一个client,什么东东呢?就是Feign.Client,可见这个也是通过自动配置类注入的,找啊找,你把FeignAutoConfiguration、FeignClientsConfiguration这几个配置类都找遍了也没发现,最后在一个FeignRibbonClientAutoConfiguration 的配置类中似乎是找到了

@Bean
@ConditionalOnMissingBean
public Request.Options feignRequestOptions() {
    return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
public Options() {
    this(10000, 60000);
}

发现这竟然是默认的… … 真是找了个寂寞!!!

到这里,没什么线索,就要思考一下,@FeignClient的注解里面配置的不是url,而是服务名称,所以这一块肯定是跟ribbon整合的

org.springframework.cloud.netflix.feign.ribbon

结果在这个包下面,找到一个 DefaultFeignLoadBalancedConfiguration!!!

@Configuration
class DefaultFeignLoadBalancedConfiguration {
	@Bean
	@ConditionalOnMissingBean
	public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
							  SpringClientFactory clientFactory) {
		return new LoadBalancerFeignClient(new Client.Default(null, null),
				cachingFactory, clientFactory);
	}
}

这里spring容器中拿到的就是这个LoadBalancerFeignClient,拿到之后也给设置到 builder 中去了

然后 Targeter targeter = get(context, Targeter.class);

显然,就是从context 中拿到一个Targeter,然后我们在FeignAutoConfiguration这个配置类里面可以找到这个Targeter 的自动配置,代码如下

@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Targeter feignTargeter() {
        return new HystrixTargeter();
    }
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Targeter feignTargeter() {
        return new DefaultTargeter();
    }
}

居然有两个,那么用的是哪一个呢?实际上是 HystrixTargeter,因为 feign.hystrix.HystrixFeign 这个类存在的,就在 feign-hystrix-9.7.0.jar 这个jar包里面

HystrixFeign

那么既然用的是这个所谓的HystrixFeign,是不是可以猜想,feign调用的时候会默认和Hystrix整合的,然后可以去使用Hystrix的熔断、降级的特性

其实springcloud的几个组件,都是有一些整合在里面的,比如说feign 就整合了 ribbon和hystrix用来做负载和熔断,ribbon呢,又跟eureka整合,去获取和更新服务列表,正所谓一通百通,也是这个意思,理解了其中一些组件,再看另外的组件,就不会吃力了

在这里插入图片描述

这里明显openFeign默认是集成Hystrix的,其实在早先的版本,springcloud-feign组件的时候,是需要配置一个feign.hystrix.enabled参数,才会使用 HystrixTargeter 的,否则使用的就是feign 默认的 DefaultTargeter,现在openfeign 对其做了改进

代码接着走 targeter.target(this, builder, context, target);

断点就来到了 HystrixTargeter的 target方法

@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.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
    SetterFactory setterFactory = getOptional(factory.getName(), context,
                                              SetterFactory.class);
    if (setterFactory != null) {
        builder.setterFactory(setterFactory);
    }
    Class<?> fallback = factory.getFallback();
    if (fallback != void.class) {
        return targetWithFallback(factory.getName(), context, target, builder, fallback);
    }
    Class<?> fallbackFactory = factory.getFallbackFactory();
    if (fallbackFactory != void.class) {
        return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
    }

    return feign.target(target);
}

!(feign instanceof feign.hystrix.HystrixFeign.Builder)

但是因为我们的Feign.Builder 使用的不是Hystrix.Builder,而是默认的Feign.Builder

所以 feign.target(target) ,使用的还是默认的,没有整合Hystrix!!!

可以我们之前的猜想Feign默认整合 Hystrix组件,并不是正确的!还是需要配置feign.hystrix.enabled的参数才能整合进来,分析了个寂寞

接着走

public <T> T target(Target<T> target) {
    return build().newInstance(target);
}
@SuppressWarnings("unchecked")
@Override
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);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

    for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
        defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}

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

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) {
       BuildTemplateByResolvingArgs buildTemplate;
       if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
         buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
       } else if (md.bodyIndex() != null) {
         buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
       } else {
         buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
       }
       result.put(md.configKey(),
                  factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
     }
     return result;
    }
  }

key.type() 这个Target 就是之前传入的HardCodedTarget,里面的type 就是feignclient 的接口类型

List metadata = contract.parseAndValidatateMetadata(key.type());
解析的metadata 是一个MethodMetadata的集合,而这个MethodMetadata 里面包含的就是每个接口方法的一些元数据信息,最主要包含了方法名、返回值类型、http发送模板还有一些其他的东西

MethodMetadata
看下这个template GET /test_1/{param} HTTP/1.1 这不就是SpringMvcContract组件从spring的注解中解析出来的吗?

接着会使用我们之前传入Feign.Builder 中的Encoder、Decoder、Contract、包括这个target ,去解析接口方法,为什么会使用这些组件呢?很明白的,接口注解都是spring web mvc的注解,我们这个Contract是啥呢?就是SpringMvcContract!!!

解析之后,拿到一个nameToHandler ,可以看到,这是一个Map,map的key就是方法名,value就是SynchronousMethodHandler对象,这是一个代理方法处理组件,就是最终用来处理这个接口方法的组件,可以看到,里面注入了很多需要的组件(target、client、retryer、日志、编解码器等)

SynchronousMethodHandler

result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder));

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

targetToHandlersByName的apply方法,返回的就是这个result

然后接着走 methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));

很明显,这个代码,就是把nameToHandler 中的SynchronousMethodHandler 这个方法处理的组件转移到了新的Map(methodToHandler)中罢了,只不过先前的Map key是方法名称,现在是Method 对象而已

InvocationHandler handler = factory.create(target, methodToHandler);

这里不多说,搞一个InvocationHandler ,是准备创建动态代理使用,因为稍微有点java基本的都会知道,java的动态代理,分jdk动态代理和cglib对象代理,针对接口的一般使用jdk动态代理,而底层使用的就是这个InvocationHandler

@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
    return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
private final Map<Method, MethodHandler> dispatch;
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
    this.target = checkNotNull(target, "target");
    this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}

这里注意,把之前转移的map methodToHandler又赋值给了dispatch 这个map,记住这个点,也就是说现在dispatch 里面,key是接口的方法对象,而value就是方法处理组件SynchronousMethodHandler

T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

这个就不解释了,因为是在没啥可说的,就是利用jdk动态代理创建一个代理对象,然后就把这个对象给返回了

ok,这篇文章就到这里,下一讲会来探究一下Controller里面调用代理对象的方法会怎么处理,拭目以待!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值