Spring Cloud Open Feign系列【15】Feign动态代理源码分析

Spring Cloud Open Feign系列【15】Feign动态代理源码分析,第1张

Spring Cloud Open Feign系列【15】Feign动态代理源码分析

文章目录
  • 前言
  • 核心类
    • Target接口
    • Client
    • FeignContext
    • Feign
    • Targeter
  • 流程分析
    • 1. 获取对象
    • 2. 创建上下文、构建者
    • 3. 创建Target
    • 4. 生成代理对象
  • 总结

前言

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

核心类 Target接口

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

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

Target接口有两个实现类,EmptyTarget和HardCodedTarget。

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

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

Client

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

public interface Client {
	// 
    Response execute(Request var1, Options var2) throws IOException;
}

该接口的默认实现类为Default,使用的是HttpURLConnection,性能较低。

如果使用了OkHttp,则会使用OkHttpClient,一般实际开发,应当替换掉Default。

FeignContext

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

public class FeignContext extends NamedContextFactory {
	
	// 只从子容器寻找bean,返回bean实例
	@Nullable
	public  T getInstanceWithoutAncestors(String name, Class type) {
		return BeanFactoryUtils.beanOfType(getContext(name), type);
	}
	// 只从子容器寻找bean,返回Map
	@Nullable
	public  Map getInstancesWithoutAncestors(String name, Class type) {
		return getContext(name).getBeansOfType(type);
	}

}

Feign

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

    public abstract  T newInstance(Target 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 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 客户端对象就是这个类。

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

        public Map apply(Target target) {
        	// 方法元数据,Contract 会对传入的接口进行解析验证,看注解的使用是否符合规范,然后返回给我们接口上各种相应的元数据。
            List metadata = this.contract.parseAndValidatemetadata(target.type());
            // 将方法名称,方法处理器放入一个Map 中,,MethodHandler则是包含此方法执行需要的各种信息
            Map 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方法,它接收FeignClientFactoryBean、Feign.Builder、FeignContext、Target.HardCodedTarget类型的参数。

interface Targeter {
     T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget target);
}

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

他有以下几个实现类:

DefaultTargeter是其默认实现类,直接调用了Builder中实际是Feign 子类实现的newInstance方法创建实例。

    public  T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget target) {
        return feign.target(target);
    }

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

流程分析 1. 获取对象

之前分析过,@FeignClient上的所有信息,都会注册为FeignClientFactoryBean类型的BeanDefinition,学过Spring 的应该知道,FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,其getObject()方法会返回创建的Bean实例。

在Spring 容器进行实例化的时候,就会进入到FeignClientFactoryBean的getObject()方法,其实际调用的是本身的getTarget()。首先可以看到FeignClientFactoryBean中的相关属性:

2. 创建上下文、构建者

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

     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、名称。

4. 生成代理对象

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

    protected  T loadBalance(Builder builder, FeignContext context, HardCodedTarget 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 target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget 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);
            }
        }
    }

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

        public  T target(Target target) {
        	// 直接newInstance
            return this.build().newInstance(target);
        }

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

    public  T newInstance(Target target) {
    	// 创建方法处理器,每个方法对应一个MethodHandler 实现类
        Map nameToHandler = this.targetToHandlersByName.apply(target);
        Map methodToHandler = new linkedHashMap();
        List 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中。

总结

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5696812.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存