Feign源码之FeignClientFactoryBean创建动态代理

17 篇文章 2 订阅
15 篇文章 1 订阅

在上一篇文章《Feign源码之@EnableFeignClients》

  1. 通过registerClientConfiguration我们知道了每一个client的configuration都是作为FeignClientSpecification的一个属性,真正被注入的类型是FeignClientSpecification。
  2. registerFeignClients方法注册的bean是FeignClientFactoryBean类型,还不是feign最终使用的动态代理对象。
    本文则一探究竟,这两个类到底有什么用?

FeignClientFactoryBean

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
		ApplicationContextAware {

可以看到该类实现了FactoryBean接口,那就看一下他的getObject()方法

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

该方法调用getTarget方法

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

		if (!StringUtils.hasText(this.url)) {
			if (!this.name.startsWith("http")) {
				url = "http://" + this.name;
			}
			else {
				url = this.name;
			}
			url += cleanPath();
			return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
					this.name, url));
		}
		if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
			this.url = "http://" + this.url;
		}
		String url = this.url + cleanPath();
		Client client = getOptional(context, Client.class);
		if (client != null) {
			if (client instanceof LoadBalancerFeignClient) {
				// not load balancing because we have a url,
				// but ribbon is on the classpath, so unwrap
				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));
	}

获取上下文环境FeignContext

首先获得context,因为该类实现了ApplicationContextAware接口,会使用setApplicationContext方法给成员变量applicationContext赋值获得context上下文(注意与后面讲的FeignContext区别开来,前者是spring容器共用,后者的作用是为每一个feignClint单独创建一个AnnotationConfigApplicationContext上下文环境

public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.applicationContext = context;
	}

从上下文直接获得FeignContext的bean

FeignContext context = applicationContext.getBean(FeignContext.class);

打开FeignContext,继承了NamedContextFactory,从之前的ribbon源码学习中我们可以了解到,NamedContextFactory可以为每一个ribbonClient创建单独的context,做到每个client有自己单独的上下文环境,不被别的client配置影响。那么在这里作用是为每一个feignClint单独创建一个AnnotationConfigApplicationContext上下文环境

public class FeignContext extends NamedContextFactory<FeignClientSpecification> {

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

}

其实contexts的底层还是一个map,key是contextId,value存放的是AnnotationConfigApplicationContext

private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

其getContext方法就是直接从map中取值

protected AnnotationConfigApplicationContext getContext(String name) {
		if (!this.contexts.containsKey(name)) {
			synchronized (this.contexts) {
				if (!this.contexts.containsKey(name)) {
					this.contexts.put(name, createContext(name));
				}
			}
		}
		return this.contexts.get(name);
	}

若map中不存在,则调用createContext方法为当前FeignClient创建一个context出来,key即位contextId,关于contextId的取值逻辑可以参考文首链接重温一遍

protected AnnotationConfigApplicationContext createContext(String name) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		if (this.configurations.containsKey(name)) {
			for (Class<?> configuration : this.configurations.get(name)
					.getConfiguration()) {
				context.register(configuration);
			}
		}
		for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
			if (entry.getKey().startsWith("default.")) {
				for (Class<?> configuration : entry.getValue().getConfiguration()) {
					context.register(configuration);
				}
			}
		}
		context.register(PropertyPlaceholderAutoConfiguration.class,
				this.defaultConfigType);
		context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
				this.propertySourceName,
				Collections.<String, Object> singletonMap(this.propertyName, name)));
		if (this.parent != null) {
			// Uses Environment from parent as well as beans
			context.setParent(this.parent);
		}
		context.setDisplayName(generateDisplayName(name));
		//刷新容器
		context.refresh();
		return context;
	}

从这里可以看到configurations的身影了

private Map<String, C> configurations = new ConcurrentHashMap<>();

那么这个ConcurrentHashMap的集合是什么时候初始化的呢?

FeignAutoConfiguration

打开FeignAutoConfiguration,找到如下代码,同时也知道了容器中FeignContext是如何注册的

@Bean
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();
		context.setConfigurations(this.configurations);
		return context;
	}

setConfigurations方法对configurations进行了赋值

public void setConfigurations(List<C> configurations) {
		for (C client : configurations) {
			this.configurations.put(client.getName(), client);
		}
	}

问题又来了,参数传进来的configurations是怎么来的呢?

@Autowired(required = false)
	private List<FeignClientSpecification> configurations = new ArrayList<>();

可以看到,使用@Autowired自动注入了FeignClientSpecification的list,而FeignClientSpecification的注册我们已经在上一篇文章讲过,不再赘述

创建Feign的建造器Builder

protected Feign.Builder feign(FeignContext context) {
//从FeignContext中获取到了FeignLoggerFactory组件
		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,从context中获得encoder,decoder以及contract,都设置到了Feign.Builder组件中,那么他们的实现类是如何注册的呢?发现在同包下面还有一个FeignClientsConfiguration配置类,那就点进去看看。

@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
	}

	@Bean
	@ConditionalOnMissingBean
	public Encoder feignEncoder() {
		return new SpringEncoder(this.messageConverters);
	}

	@Bean
	@ConditionalOnMissingBean
	public Contract feignContract(ConversionService feignConversionService) {
		return new SpringMvcContract(this.parameterProcessors, feignConversionService);
	}
@Bean
	@ConditionalOnMissingBean(FeignLoggerFactory.class)
	public FeignLoggerFactory feignLoggerFactory() {
		return new DefaultFeignLoggerFactory(logger);
	}

Decoder、Encoder、Contract、FeignLoggerFactory的实际类型都很顺利的找到了。
因为Feign最终还是要发送HTTP请求,这⾥就难免要涉及到序列化和反序列化、这个过程就会牵扯到编码和解码的过程,Encoder和Decoder就派上⽤场了。
因为Feign⾃带的注解@FeignClient、以及SpringMVC注解它们是被谁处理的呢?Contract的实现类SpringMVCContract就是来解析它们的,解析所有的注解信息、然后拼凑成⼀个完整的HTTP请求所需要的信息。

最后调用过configureFeign方法把配置文件的一些配置绑定到feign的配置类上。

protected void configureFeign(FeignContext context, Feign.Builder builder) {
		FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
		if (properties != null) {
		
			if (properties.isDefaultToProperties()) {
				configureUsingConfiguration(context, builder);
				configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
				configureUsingProperties(properties.getConfig().get(this.contextId), builder);
			} else {
				configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
				configureUsingProperties(properties.getConfig().get(this.contextId), builder);
				configureUsingConfiguration(context, builder);
			}
		} else {
			configureUsingConfiguration(context, builder);
		}
	}
  1. defaultToProperties默认为true
  2. configureUsingConfiguration(context, builder);读取配置类的信息
  3. configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);配置文件的默认信息
  4. configureUsingProperties(properties.getConfig().get(this.contextId), builder);配置文件中指定服务的配置信息
  5. 默认情况下先读取配置来的信息,则配置文件的信息优先级更高,可以通过defaultToProperties配置为false,提高配置文件的优先级到最高

服务的配置信息优先级是要⽐默认配置要⾼的,所以也会覆盖默认的配置信息。

生成动态代理对象

至此,终于得到了builder对象,接下来执行代码到

return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
					this.name, url));

给builder对象设置了client属性

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

接下来,我们看到它直接从容器中获取了⼀个Targeter对象,这个对象是HystrixTargeter对象,在FeignAutoConfiguration 中找到

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

然后直接调⽤target⽅法,如下图所示:

class DefaultTargeter implements Targeter {

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
						Target.HardCodedTarget<T> target) {
		return feign.target(target);
	}
}


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

首先调用build方法,new 了一个SynchronousMethodHandler.Factory,创建了一个ReflectiveFeign对象并返回,这两个对象的作用,这里先不讲

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

继续查看newInstance方法

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

进⼊到apply⽅法中,发现contract通过调⽤parseAndValidateMetadata⽅法得到了List,也就是接⼝中的所有⽅法的元数据信息,然后遍历这些⽅法,通过factory创建MethodHandler,⽽这⾥的 factory就是我们前⾯在build⽅法中看到的SynchronousMethodHandler.Factory 。

public Map<String, MethodHandler> apply(Target key) {
			//contract通过调⽤parseAndValidateMetadata⽅法得到了接⼝中的所有⽅法的元数据信息
            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;
        }

接下来可以看到InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler);这两行代码,终于创建了动态代理。

接下来回到getTarget方法

if (!StringUtils.hasText(this.url)) {
			if (!this.name.startsWith("http")) {
				url = "http://" + this.name;
			}
			else {
				url = this.name;
			}
			url += cleanPath();
			return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
					this.name, url));
		}

以上是url为空的时候,使用负载均衡来调用,那么如果url属性不为空的时候,因为有ribbon的存在,需要解封装

if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
			this.url = "http://" + this.url;
		}
		String url = this.url + cleanPath();
		Client client = getOptional(context, Client.class);
		if (client != null) {
			if (client instanceof LoadBalancerFeignClient) {
				// not load balancing because we have a url,
				// but ribbon is on the classpath, so unwrap
				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));

最后仍然是调用targeter.target方法获得代理对象。

最后,我们上文提到了SynchronousMethodHandler和ReflectiveFeign这两个对象,下文我们将继续探索。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值