16、Feign动态代理源码分析

前言

在之前分析了@EnableFeignClients@FeignClient 是如何进行扫描及读取Feign 接口信息的,最后注解上的信息是被加载为BeanDefinition放入了BeanDefinitionRegistry中,接下来就是加载为Bean 对象了,依据下图(来自于百度)Feign 基本原理,分析下Feign 动态代理机制。
 

核心类

Target接口

Target接口封装了feign 客户端接口的一些信息,并提供了将RequestTemplate请求模板转化为实际请求的Request的方法。

public interface Target<T> {

    // 接口类型
    Class<T> type();
    // 客户端名称
    String name();
    //访问URL 
    String url();
    // 将RequestTemplate 请求模板转化为实际请求的Request 
    Request apply(RequestTemplate var1);
}

Target接口有两个实现类,EmptyTargetHardCodedTarget

EmptyTarget意为空的目标,其没有URL属性。

HardCodedTarget意为硬编码目标,Feign 中采用的是这个。

Client

Client就是Feign 直接执行请求的接口,可以看到传入了Request请求对象,及一个Options对象(超时配置)。

public interface Client {

    // 
    Response execute(Request var1, Options var2) throws IOException;
}

该接口的默认实现类为Default,使用的是HttpURLConnection,性能较低。
&nbsp;
如果使用了OkHttp,则会使用OkHttpClient,一般实际开发,应当替换掉Default

FeignContext

FeignContext继承了NamedContextFactory,用于创建Spring 子容器,每个客户端都对应了唯一的一个FeignContext

public class FeignContext extends NamedContextFactory<FeignClientSpecification> {

    // 只从子容器寻找bean,返回bean实例
    @Nullable
    public <T> T getInstanceWithoutAncestors(String name, Class<T> type) {

        return BeanFactoryUtils.beanOfType(getContext(name), type);
    }
    // 只从子容器寻找bean,返回Map<bean名称,bean实例>
    @Nullable
    public <T> Map<String, T> getInstancesWithoutAncestors(String name, Class<T> type) {

        return getContext(name).getBeansOfType(type);
    }

}

Feign

Feign是一个抽象类,提供了一个生成接口代理对象的核心方法。

    public abstract <T> T newInstance(Target<T> var1);

还提供了一个构建者Builder,用于创建Feign 接口实例

        public Feign build() {

            // Client 实例
            Client client = (Client)Capability.enrich(this.client, this.capabilities);
            // 重试器
            Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);
            // 拦截器
            List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {

                return (RequestInterceptor)Capability.enrich(ri, this.capabilities);
            }).collect(Collectors.toList());
            // 日志
            Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);
            // 模板契约
            Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);
            // 参数
            Options options = (Options)Capability.enrich(this.options, this.capabilities);
            // 编码器
            Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);
            // 解码器
            Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);
            InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
            QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
            Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
            ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
            // 返回示例
            return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
        }

ReflectiveFeign实现了Feign 接口,重写了newInstance方法,实际生产的Feign 客户端对象就是这个类。

ParseHandlersByNameReflectiveFeign的静态内类,意思是通过名称解析处理器,它只有一个apply方法:

        public Map<String, MethodHandler> apply(Target target) {

            // 方法元数据,Contract 会对传入的接口进行解析验证,看注解的使用是否符合规范,然后返回给我们接口上各种相应的元数据。
            List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(target.type());
            // 将方法名称,方法处理器放入一个Map 中,,MethodHandler则是包含此方法执行需要的各种信息
            Map<String, MethodHandler> result = new LinkedHashMap();
            Iterator var4 = metadata.iterator();
            // 构建
            while(var4.hasNext()) {

                MethodMetadata md = (MethodMetadata)var4.next();
                Object buildTemplate;
                if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {

                    buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
                } else if (md.bodyIndex() != null) {

                    buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
                } else {

                    buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.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(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
                }
            }

            return result;
        }
    }

Targeter

Targeter定义了target方法,它接收FeignClientFactoryBeanFeign.Builder、FeignContextTarget.HardCodedTarget类型的参数。

interface Targeter {

    <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target);
}

最终生成的Bean 对象,是由该接口返回。

他有以下几个实现类:
&nbsp;
DefaultTargeter是其默认实现类,直接调用了Builder中实际是Feign 子类实现的newInstance方法创建实例。

    public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {

        return feign.target(target);
    }

还有一个HystrixTargeter,是可以创建基于Hystrix的目标对象,默认使用的是这个。
&nbsp;

流程分析

1. 获取对象

之前分析过,@FeignClient上的所有信息,都会注册为FeignClientFactoryBean类型的BeanDefinition,学过Spring 的应该知道,FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,其getObject()方法会返回创建的Bean实例。
&nbsp;
在Spring 容器进行实例化的时候,就会进入到FeignClientFactoryBeangetObject()方法,其实际调用的是本身的getTarget()。首先可以看到FeignClientFactoryBean中的相关属性:
&nbsp;

2. 创建上下文、构建者

getTarget()中,是根据是否配置了URL属性,创建不同的代理对象,没有URL,说明是需要从注册中心获取远程地址,使用的是loadBalance方法加载动态的负载均衡。

    <T> T getTarget() {

        // 获取FeignContext ,每个Feign 客户端都对应一个context 
        FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
        // 获取Feign 构建者
        Builder builder = this.feign(context);
        if (!StringUtils.hasText(this.url)) {

            if (!this.name.startsWith("http")) {

                this.url = "http://" + this.name;
            } else {

                this.url = this.name;
            }
            // 拼接URL -=》http://order-service
            this.url = this.url + this.cleanPath();
            return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
        } else {

            // 判断是否配置了url 属性,配置了则直接根据URL创建代理对象
            if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {

                this.url = "http://" + this.url;
            }
            String url = this.url + this.cleanPath();
            Client client = (Client)this.getOptional(context, Client.class);
            // 省略.....
            return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
        }
    }

3. 创建Target

loadBalance方法中,首先会创建一个HardCodedTarget对象,其中封装了Feign 客户端接口的类型、请求url、名称。

&nbsp;

4. 生成代理对象

接着进入到loadBalance方法,生成代理对象。

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

        // 创建客户端
        Client client = (Client)this.getOptional(context, Client.class);
        if (client != null) {

            // 
            builder.client(client);
            // 生成代理对象
            Targeter targeter = (Targeter)this.get(context, Targeter.class);
            return targeter.target(this, builder, context, target);
        } else {

            throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
        }
    }

默认使用的是HystrixTargeter

    public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {

        // 没有使用hystric 直接创建Feign 的
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {

            return feign.target(target);
        } else {

                // 省略
                // 使用的hystric 会设置fallback  
                return fallbackFactory != Void.TYPE ? this.targetWithFallbackFactory(name, context, target, builder, fallbackFactory) : feign.target(target);
            }
        }
    }

直接调用的是构建者BuildernewInstance方法为目标创建实例:

        public <T> T target(Target<T> target) {

            // 直接newInstance
            return this.build().newInstance(target);
        }

newInstance方法中,会为当前接口下每个方法创建方法处理器,然后再使用Proxy创建动态代理对象。

    public <T> T newInstance(Target<T> target) {

        // 创建方法处理器,每个方法对应一个MethodHandler 实现类
        Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;
        // 循环方法Method 创建处理器
        for(int var7 = 0; var7 < var6; ++var7) {

            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {

                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 = this.factory.create(target, methodToHandler);
        // 创建代理对象
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{

     target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();

        while(var12.hasNext()) {

            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }

        return proxy;
    }

最终,该实例放入到了IOC中。
&nbsp;

总结

&nbsp;