SpringCloud系列之Feign-6.Feign上下文构建解析

1.Feign的上下文还有反射代理机制就在于Feign的上下文解析的过程,所以这块也是Feign的核心模块
2.所有feign的动态代理对象都是通过newInstance创建的

1.首先在构建上下文的入口是在FeignClientFactoryBean的getObject方法


image.png

2.首先第一步就是构建了FeignContext类

public class FeignContext extends NamedContextFactory {

    public FeignContext() {
        super(FeignClientsConfiguration.class, "feign", "feign.client.name");
    }

}

3.然后我们进入到feign这个方法中看下:

protected Feign.Builder feign(FeignContext context) {
        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
        Logger logger = loggerFactory.create(this.type);

        // @formatter:off
        Feign.Builder builder = get(context, Feign.Builder.class)
                // required values
                .logger(logger)
                .encoder(get(context, Encoder.class))
                .decoder(get(context, Decoder.class))
                .contract(get(context, Contract.class));
        // @formatter:on

        configureFeign(context, builder);

        return builder;
    }

可以看到首先获取两个日志对象一个FeignLoggerFactory,一个Logger,然后就是把这两个日志对象赋值给feign这个对象,我们看的Builder其实类似于方法糖,让赋值更加方便而已,类似于lombok中的@builder注解一样。

除了赋值日志对象以外,还有加密解密的一个赋值操作,这个是url解析过程中也是非常常见的。然后其他的我们暂时不关注,只要知道这里是初始化了feign对象,一个空的对象,构造器。

4.然后我们继续在主方法往下走

image.png

在上一个帖子上我们注意到url是空的,那么在这里就需要使用到了,从源码中看到如果url是空的,那么就使用服务名称作为ip了,然后如果不是http开头,这里就会给一个http的前缀上去,因为feign也是基于eureka的http接口的。

我们在看下下一个方法啊 cleanPaht();

private String cleanPath() {
        String path = this.path.trim();
        if (StringUtils.hasLength(path)) {
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (path.endsWith("/")) {
                path = path.substring(0, path.length() - 1);
            }
        }
        return path;
    }

这里其实还是拼装url的操作,在@FeignClient注解中有一个path的属性,这个属性干嘛用的呢,如图:


image.png

而这个path我们最后是不需要/的,所以这里其实就是格式化一下而已,正确的拼接好url的操作。

5.主方法继续

这里就是构造动态代理对象的地方了。


image.png

我们点进去看:

image.png

可以看到首先调用了getOptional这个方法,然后使用了

context.getInstance(this.name, type);

传入了一个调用的服务名称就是 eureka-client这个服务名称,还有一个 feign.Client的类。

其实这个方法就是从上下文中拿到调用了这个服务的接口作为一个client。

然后把client放到Feign对象中,再构建一个Targeter类

6.然后我们进入targeter.target这个方法看看这个最核心的地方

image.png

这块逻辑做了一个判断,看这个feign是不是hystrix的feign,如果是就走上面的逻辑,否则就走下面的逻辑,降级熔断啊之类的逻辑,但我们注意到不论走哪个,最终都会走feign.target(target);

7.然后我们进入 feign.target 这个方法:


image.png

我们继续进入newInstance这个方法:

image.png

上面的三个方法没看懂,我们暂且跳过,看到target通过断点我们可以知道,type就是IService,getMethods就是获取到了FeignClient那个接口下面的所有方法了,现在就是遍历所有方法了,我们现在懂点看下遍历中做的逻辑是什么

8.首先进行判断,看他是对象吗,显然这个是方法不是对象所以跳过,然后Util.isDefault(method)我们看下:

 public static boolean isDefault(Method method) {
    // Default methods are public non-abstract, non-synthetic, and non-static instance methods
    // declared in an interface.
    // method.isDefault() is not sufficient for our usage as it does not check
    // for synthetic methods.  As a result, it picks up overridden methods as well as actual default methods.
    final int SYNTHETIC = 0x00001000;
    return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) ==
            Modifier.PUBLIC) && method.getDeclaringClass().isInterface();
  }

看着大致意思就是判断这个是不是接口而且修饰符必须是这几种;

所以这个判断也没进去,最后执行了下面这个方法:

        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));

我们查看下Feign.configKey(target.type(), method) 这个是什么:

image.png

发现是这个FeignClient接口的名称#方法名称;

所以这里就在methodToHandler.put如一个方法,还有方法的Handler,所以说这个接口虽然没有实现类,但是转发到了其他的实现类中,那么这个实现类到底是什么,我们继续往下面看:

image.png

然后就把这个handler作为参数创建了一个代理proxy并返回,然后如果我们通过Feign访问的话其实访问的就是代理proxy,但是最终干活的其实就是handler。

你可能感兴趣的:(SpringCloud系列之Feign-6.Feign上下文构建解析)