第二次Feign学习总结

文章目录

  • 前言
  • 一、概要
  • 二、第一个入口
  • 三、第二个入口
    • 3.1 FeignClientsRegistrar
    • 3.2 FeignContext
    • 3.3 FeignClientFactoryBean
    • 3.4 HystrixTargeter
    • 3.5 Feign.Builder
    • 3.6 ReflectiveFeign
  • 四、代理对象对远程调用的实现
    • 4.1 InvocationHandlerFactory.Default
    • 4.2 ReflectiveFeign.FeignInvocationHandler
    • 4.3 SynchronousMethodHandler
    • 4.4 LoadBalancerFeignClient
    • 4.5 AbstractLoadBalancerAwareClient
    • 4.6 FeignLoadBalancer
    • 4.7 Client.Default
  • 总结


前言

最近把Spring的IOC和AOP以及Sprongboot相关的知识又学习了一遍,在这个基础上再次看了Feign源码感觉有了新的体会,整体思路更加清晰了。这里做一个学习总结


一、概要

从Feign使用层面来说,主要有两个步骤
1 给启动类上加@EnableFeignClients注解
2 定义一个接口,配置@FeignClient注解,并且给接口中的每个方法都加上mvc注解齐活了。
配置完成spring容器会帮我们为这个接口创建一个代理类,调用代理类的方法即可实现远程调用,就好像调用本地的service一样方便。
要想搞明白feign是如何实现这个能力的,主要需要搞清除两个问题
1 代理类是何时被创建的
2 代理类中如何实现的远程调用
下面就一点一点的找出这两个问题的答案

二、第一个入口

feign有两个入口,他们相辅相成,其中的一个入口来至springboot自动装配。springboot启动后默认会装配一些东西,其中既有feign相关的
如下:
spring-cloud-openfeign-core-2.2.2.RELEASE.jar包下的spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

这里自动装配了很多东西。装配的内容会根据各个项目的需要进行调整。以下是我本地项目的装配情况,装配内容也不少这里介绍一些关键的目的是为了承接后边的内容,下面分别看下【FeignAutoConfiguration、FeignRibbonClientAutoConfiguration、DefaultFeignLoadBalancedConfiguration】这三个自动装配类

FeignAutoConfiguration类

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
//【FeignClientProperties】feign.client前缀开头的参数 这个对象在另一个入口中会用到
@EnableConfigurationProperties({ FeignClientProperties.class,
		FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
	//一堆FeignClientSpecification对象 这些对象是在另一个入口注入到容器的 在这里被取出来了
	//为了放入FeignContext对象中
    @Autowired(required = false)
	private List<FeignClientSpecification> configurations = new ArrayList<>();
	//容器工厂 该类为每一个客户端(被@FeignClient注释的接口) 创建一个子容器
	@Bean
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();
		context.setConfigurations(this.configurations);
		return context;
	}
	
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
	protected static class HystrixFeignTargeterConfiguration {
	    //该类用于创建代理类用的 另一个入口中会用到
		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
			return new HystrixTargeter();
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
	protected static class DefaultFeignTargeterConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
			return new DefaultTargeter();
		}

	}
}

FeignRibbonClientAutoConfiguration

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
		matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
		OkHttpFeignLoadBalancedConfiguration.class,
		//主要看这个类 
		DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
	。。。。略
	//这里注入了LoadBalancerFeignClient的默认配置 后续会去容器里取
	@Bean
	@ConditionalOnMissingBean
	public Request.Options feignRequestOptions() {
		return LoadBalancerFeignClient.DEFAULT_OPTIONS;
	}
}

DefaultFeignLoadBalancedConfiguration

@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
	//这里注册了LoadBalancerFeignClient类到容器 另一个入口需要
	@Bean
	@ConditionalOnMissingBean
	public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
			SpringClientFactory clientFactory) {
		//记住这第一个参数Client.Default 在最最最后边会用到 	
		return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
				clientFactory);
	}
}

这里总结下,3个类中向容器注册了不少bean,其中主要有5个bean需要重点关注下
1 FeignClientProperties feign配置对象【对应application.yml中feign.client开头的配置】
2 FeignContext 容器工厂,可以创建容器。内部放了一个FeignClientSpecification集合,集合是在第二个入口注册的
3 HystrixTargeter 创建代理对象用的
4 LoadBalancerFeignClient 负载均衡客户端 另一个入口中用到了,记住其构造器的第一个参数Client.Default。
5 LoadBalancerFeignClient.Options其本身就是LoadBalancerFeignClient.DEFAULT_OPTIONS
第二个入口才是重点,如果把第一个入口比做提供了一把零件的话,第二个入口就是开启了对这些零件的组装和使用

三、第二个入口

要想使用Feign必须在启动类中加入@EnableFeignClients注解,这个注解中最主要的就是向容器中注入了一个FeignClientsRegistrar注册器,其registerBeanDefinitions方法就是Feign的第二个入口。代理对象的创建就是在这个入口中发生的

3.1 FeignClientsRegistrar

class FeignClientsRegistrar
		implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
	//入口方法
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		//向容器中注册了一个default.开头的FeignClientSpecification 和第一入口相呼应	
		registerDefaultConfiguration(metadata, registry);
		//主要跟这个方法
		registerFeignClients(metadata, registry);
	}
	//依次将@FeignClient注释的接口定义信息注册到容器
	public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		//构造一个扫描器	
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);
        。。。略
        //构造一个寻找FeignClient.class注解的过滤器
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
				FeignClient.class);
		final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
		。。。略
		//这里暂时可以理解为在启动类所在的包及其子孙包下 用过滤器进行寻找
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidateComponents = scanner
					.findCandidateComponents(basePackage);
			//便利寻找到的结果		
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
					// verify annotated class is an interface
					AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
					//获取注解中的信息
					AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
					Assert.isTrue(annotationMetadata.isInterface(),
							"@FeignClient can only be specified on an interface");
					//获取注解中的参数
					Map<String, Object> attributes = annotationMetadata
							.getAnnotationAttributes(
									FeignClient.class.getCanonicalName());
					//这个name是基于注解配置而来 如果前者为空 那么顺被取后置 优先级如下
					//contextId>value>name>serviceId
					String name = getClientName(attributes);
					//这里会创建一个以 客户端name.开头的FeignClientSpecification 和第一入口 相呼应
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));
					//重点看这里
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}
	//将bean定义信息注册到容器
	//最终定义bean通过FeignClientFactoryBean工厂bean进行创建
	private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		//重点是这里指定了FeignClientFactoryBean来创建bean
		//接下来跟其getObject方法 看看其如何构建bean 构建代理对象的逻辑就在其中
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		validate(attributes);
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		//顺位 serviceId>name>value
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		//顺位 contextId>serviceId>name>value
		String contextId = getContextId(attributes);
		definition.addPropertyValue("contextId", contextId);
		definition.addPropertyValue("type", className);
		definition.addPropertyValue("decode404", attributes.get("decode404"));
		definition.addPropertyValue("fallback", attributes.get("fallback"));
		definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

		String alias = contextId + "FeignClient";
		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

		boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
																// null

		beanDefinition.setPrimary(primary);

		String qualifier = getQualifier(attributes);
		if (StringUtils.hasText(qualifier)) {
			alias = qualifier;
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				new String[] { alias });
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}
}

registerBeanDefinitions主要做了两件事
1 向容器中注册了一个default.开头的FeignClientSpecification 和第一入口相呼应
2 注册客户端
–2.1 在启动类以及其子孙包下寻找被@FeignClient注释的class
–2.2 为每一个@FeignClient注释的class构建BeanDefinition,并且指定factoryBean为FeignClientFactoryBean
通过IOC的bean创建流程可以知道bean最终会通过FeignClientFactoryBean的getObject方法来创建的

3.2 FeignContext

在看FeignClientFactoryBean之前这里先对FeignContext做下总结,因为后边会频繁的使用这个对象。该对象是在第一个入口中注册的

public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
	public FeignContext() {
		super(FeignClientsConfiguration.class, "feign", "feign.client.name");
	}
}

只有一个构造器,内部调用了父类构造器。这里要记住其调用父类构造器时的第一个参数FeignClientsConfiguration,这里存放着子容器的默认配置,后边会用到。
重点看下NamedContextFactory

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
		implements DisposableBean, ApplicationContextAware {
	
	//这里可以看到上边的FeignClientsConfiguration.class被赋予到了defaultConfigType中
	private Class<?> defaultConfigType;
	public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
			String propertyName) {
		this.defaultConfigType = defaultConfigType;
		this.propertySourceName = propertySourceName;
		this.propertyName = propertyName;
	}

	//因为该类是一个ApplicationContextAware 所以其在初始化时容器会将自己注入进来 这个容器时springboot启动时创建的容器 后边会被设置成客户端容器的父容器
	private ApplicationContext parent;
	//用于保存子容器的集合 key是容器id
	private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
	//还记得入口一中 的那些configuration吗 会调用setConfigurations放进来
	private Map<String, C> configurations = new ConcurrentHashMap<>();
	public void setConfigurations(List<C> configurations) {
		for (C client : configurations) {
			this.configurations.put(client.getName(), client);
		}
	}

	//跟到这个方法
	public <T> T getInstance(String name, Class<T> type) {
	    //跟getContext
		AnnotationConfigApplicationContext context = getContext(name);
		if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
				type).length > 0) {
			return context.getBean(type);
		}
		return null;
	}

	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);
	}
    //创建容器
	protected AnnotationConfigApplicationContext createContext(String name) {
	    //这里new出了容器
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		//这里会先注入@FeignClient 配置的configuration
		if (this.configurations.containsKey(name)) {
			for (Class<?> configuration : this.configurations.get(name)
					.getConfiguration()) {
				context.register(configuration);
			}
		}
		//这里会注入@EnableFeignClients中 配置的defaultConfiguration
		for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
			if (entry.getKey().startsWith("default.")) {
				for (Class<?> configuration : entry.getValue().getConfiguration()) {
					context.register(configuration);
				}
			}
		}
		//这里注册了this.defaultConfigType 也就是FeignClientsConfiguration.class
		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) {
			//这里注入了父容器
			context.setParent(this.parent);
			// jdk11 issue
			// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
			context.setClassLoader(this.parent.getClassLoader());
		}
		context.setDisplayName(generateDisplayName(name));
		//最后启动了子容器 并返回
		context.refresh();
		return context;
	}
}

看看FeignClientsConfiguration这里都配置了什么

@Configuration(proxyBeanMethods = false)
public class FeignClientsConfiguration {
	//请求转化器 来自于自动装配 http包
	@Autowired
	private ObjectFactory<HttpMessageConverters> messageConverters;
	
	@Autowired(required = false)
	private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();

	@Autowired(required = false)
	private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
	//Logger 
	@Autowired(required = false)
	private Logger logger;

	@Autowired(required = false)
	private SpringDataWebProperties springDataWebProperties;
	
	//解码器 将http响应信息转化成一个对象
	//最终会依赖解码器中的this.messageConverters中的MappingJackson2HttpMessageConverter 完成解码 
	@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new OptionalDecoder(
				new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
	}
    //编码器
	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
	public Encoder feignEncoder() {
		return new SpringEncoder(this.messageConverters);
	}

	@Bean
	@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
	@ConditionalOnMissingBean
	public Encoder feignEncoderPageable() {
		PageableSpringEncoder encoder = new PageableSpringEncoder(
				new SpringEncoder(this.messageConverters));
		if (springDataWebProperties != null) {
			encoder.setPageParameter(
					springDataWebProperties.getPageable().getPageParameter());
			encoder.setSizeParameter(
					springDataWebProperties.getPageable().getSizeParameter());
			encoder.setSortParameter(
					springDataWebProperties.getSort().getSortParameter());
		}
		return encoder;
	}

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

	@Bean
	public FormattingConversionService feignConversionService() {
		FormattingConversionService conversionService = new DefaultFormattingConversionService();
		for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
			feignFormatterRegistrar.registerFormatters(conversionService);
		}
		return conversionService;
	}
	//feign重试机制 这里可以看到默认是不重试
	@Bean
	@ConditionalOnMissingBean
	public Retryer feignRetryer() {
		return Retryer.NEVER_RETRY;
	}
	//Feign.Builder 
	@Bean
	@Scope("prototype")
	@ConditionalOnMissingBean
	public Feign.Builder feignBuilder(Retryer retryer) {
		return Feign.builder().retryer(retryer);
	}

	@Bean
	@ConditionalOnMissingBean(FeignLoggerFactory.class)
	public FeignLoggerFactory feignLoggerFactory() {
		return new DefaultFeignLoggerFactory(this.logger);
	}

	@Bean
	@ConditionalOnClass(name = "org.springframework.data.domain.Page")
	public Module pageJacksonModule() {
		return new PageJacksonModule();
	}

    //这里可以看到如果开启熔断会向容器注入HystrixFeign.builder而不是Feign.Builder
    //我这边没开启 所以没注入这个类 
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
	protected static class HystrixFeignConfiguration {

		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(name = "feign.hystrix.enabled")
		public Feign.Builder feignHystrixBuilder() {
			return HystrixFeign.builder();
		}
	}
}

这里对FeignContext做一个总结。该类主要对外提供getInstance(String name, Class type)接口。该接口会为不同的name创建独立的子容器,然后基于type去子容器中获取对应的bean。具体流程分为三步
1 首次调用该接口时,会针对name创建一个子容器,再次调用如果name是老的就不会在创建子容器,反之会创建。
2 创建的子容器默认向其注册一些configuration,使其容器中拥有一些默认的bean。其中最主要的configuration是FeignClientsConfiguration,其为容器注册了 【Logger、Decoder、Encoder 、Contract、Retryer 、Feign.Builder】等重要的bean,最终设置子容器的父容器为springboot启动时创建的容器
3 最后通过调用子容器的getBean获取bean对象

有了这些铺垫就可以继续阅读 FeignClientFactoryBean的getObject方法了

3.3 FeignClientFactoryBean

class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
		//入口方法
		@Override
		public Object getObject() throws Exception {
			return getTarget();
		}
		//从上边跟到这里
		<T> T getTarget() {
			//拿到FeignContext对象 其是在第一个入口中定义的
			FeignContext context = this.applicationContext.getBean(FeignContext.class);
			//构建build类 这里发生了子容器的创建
			// 其一需要跟这里
			Feign.Builder builder = feign(context);
	
			if (!StringUtils.hasText(this.url)) {
				if (!this.name.startsWith("http")) {
					this.url = "http://" + this.name;
				}
				else {
					this.url = this.name;
				}
				this.url += cleanPath();
				//这里创建了代理对象
				//其二需要跟这里
				return (T) loadBalance(builder, context,
						new HardCodedTarget<>(this.type, this.name, this.url));
		}
		//我当前使用的项目 没走到下边的分支 所有略了
		...}
	//构建build对象
	protected Feign.Builder feign(FeignContext context) {
	    //这里首次调用get方法 会为客户端创建子容器 跟下这个get方法
		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
		Logger logger = loggerFactory.create(this.type);

		//这里再次出现get 就不会在创建子容器了 
		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
		//将Feign需要的配置从配置对象中拿到build里
		configureFeign(context, builder);
		return builder;
	}
	
	//从容器中拿到了一个对象实例并返回 如果是null抛异常
	protected <T> T get(FeignContext context, Class<T> type) {
	    //跟到FeignContext类中 容器工厂要发挥作用了 基于容器id构建子容器
	    //这个类其实啥也没干 getInstance方法是其父类NamedContextFactory实现的 所以需要跟NamedContextFactory
		T instance = context.getInstance(this.contextId, type);
		if (instance == null) {
			throw new IllegalStateException(
					"No bean found of type " + type + " for " + this.contextId);
		}
		return instance;
	}
	//从容器中拿到了一个对象实例并返回 如果是null就返回null
	protected <T> T getOptional(FeignContext context, Class<T> type) {
		return context.getInstance(this.contextId, type);
	}


	//看下configureFeign 后续配置优先级啥的可能用得到
	protected void configureFeign(FeignContext context, Feign.Builder builder) {
		//还记得FeignClientProperties对象吗 入口一中定义了
		//所以后边的else分支理论上应该不会走
		FeignClientProperties properties = this.applicationContext
				.getBean(FeignClientProperties.class);
		if (properties != null) {
			//defaultToProperties是feign的一个配置参数默认是true
			//其主要影响的就是优先级,可以看到if和else中方法的执行顺序不同,后边会覆盖前边
			if (properties.isDefaultToProperties()) {
			    //子容器中的配置先来 优先级最低
			    // 自动其中的配置来至子容器创建时 为其添加的 其实就是来自于注解【@EnableFeignClients和@FeignClient】
				configureUsingConfiguration(context, builder);
				//默认配置即feign.client.default 开头的配置 优先级中等
				configureUsingProperties(
						properties.getConfig().get(properties.getDefaultConfig()),
						builder);
				//feign.client.{contextId} 开头的配置 优先级最高		
				configureUsingProperties(properties.getConfig().get(this.contextId),
						builder);
			}
			else {
				//这里3个方法和if中的一样 就是顺序不同
				//默认配置即feign.client.default 开头的配置 优先级最低
				configureUsingProperties(
						properties.getConfig().get(properties.getDefaultConfig()),
						builder);
				//feign.client.{contextId} 开头的配置 优先中等		
				configureUsingProperties(properties.getConfig().get(this.contextId),
						builder);
				//子容器中的优先级最高		
				configureUsingConfiguration(context, builder);
			}
		}
		else {
			configureUsingConfiguration(context, builder);
		}
	}
	//这里有个坑
	//如果FeignClientProperties.FeignClientConfiguration不同时设置ConnectTimeout和ReadTimeout将不会生效
	protected void configureUsingProperties(
			FeignClientProperties.FeignClientConfiguration config,
			Feign.Builder builder) {
		。。。。略
		if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
			builder.options(new Request.Options(config.getConnectTimeout(),
					config.getReadTimeout()));
		}
        。。。。略
	}
	//这个是从容器中获取配置的类
	protected void configureUsingConfiguration(FeignContext context,
			Feign.Builder builder) {
		Logger.Level level = getOptional(context, Logger.Level.class);
		if (level != null) {
			builder.logLevel(level);
		}
		Retryer retryer = getOptional(context, Retryer.class);
		if (retryer != null) {
			builder.retryer(retryer);
		}
		ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
		if (errorDecoder != null) {
			builder.errorDecoder(errorDecoder);
		}
		//还记得第一入口注册的默认配置吗 这里会获取到
		Request.Options options = getOptional(context, Request.Options.class);
		if (options != null) {
			builder.options(options);
		}
		Map<String, RequestInterceptor> requestInterceptors = context
				.getInstances(this.contextId, RequestInterceptor.class);
		if (requestInterceptors != null) {
			builder.requestInterceptors(requestInterceptors.values());
		}
		QueryMapEncoder queryMapEncoder = getOptional(context, QueryMapEncoder.class);
		if (queryMapEncoder != null) {
			builder.queryMapEncoder(queryMapEncoder);
		}
		if (this.decode404) {
			builder.decode404();
		}
		ExceptionPropagationPolicy exceptionPropagationPolicy = getOptional(context,
				ExceptionPropagationPolicy.class);
		if (exceptionPropagationPolicy != null) {
			builder.exceptionPropagationPolicy(exceptionPropagationPolicy);
		}
	}
	
	protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
		//还记得这个类吗 入口一中注册的 LoadBalancerFeignClient 	
		Client client = getOptional(context, Client.class);
		if (client != null) {
		    //将其放入builder
			builder.client(client);
			//还记得这个类吗 入口一中注册的 HystrixTargeter
			Targeter targeter = get(context, Targeter.class);
			//接下来进入HystrixTargeter中 继续跟进
			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?");
	}
}

这里可以发现其重要方法就是getTarget方法
该方法首先基于feign方法构建了一个Feign.Builder。跟进feign方法发现其内部主要是一个get方法,在跟get方法可以发现其底层调用的就是FeignContext的getInstance方法,其中第一个参数是contextId。到这里应该可以知道【Feign.Builder、Encoder、Decoder、Contract】这些bean其实都来自于子容器的默认配置。
然后是loadBalance方法,该方法将第一个入口中注册的LoadBalancerFeignClient和HystrixTargeter取了出来,然后执行了HystrixTargeter的target方法。

题外话:feign方法有一个configureFeign方法,这个不算主流程但是也挺有用这里总结下。
—配置优先级
(a) feign.client.defaultToProperties = true时 默认的
feign.client.{contextId}配置 > feign.client.default配置 > 子容器配置(注解中声明的)
(b)feign.client.defaultToProperties = true时 默认的
子容器配置(注解中声明的 > feign.client.{contextId}配置 > feign.client.default配置
—onfigureUsingProperties方法中可以看到
如果FeignClientProperties.FeignClientConfiguration不同时设置ConnectTimeout和ReadTimeout将不会生效
—configureUsingConfiguration方法中可以看到
会去容器中获取Request.Options信息,这个Options就是入口一中看到的默认配置

后边继续跟HystrixTargeter

3.4 HystrixTargeter

class HystrixTargeter implements Targeter {
	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget<T> target) {
		if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
		    //跟这里
		    //这里的feign是Feign.Builder
			return feign.target(target);
		}
		//这里有一把代码 但是我项目没走这个分支 应该是和熔断有关的 这里不跟了
		...}
}

HystrixTargeter本来有一些熔断的机制,由于我的项目没用熔断所以下边的分支没有走。继续跟feign.target

3.5 Feign.Builder

跟Feign.Builder

public static class Builder {
	//动态代理对象的处理器就是他
    private InvocationHandlerFactory invocationHandlerFactory =
        new InvocationHandlerFactory.Default();

    //入口方法
	public <T> T target(Target<T> target) {
	  //build会创建一个ReflectiveFeign对象
      return build().newInstance(target);
    }
	//基于build构建ReflectiveFeign
	public Feign build() {
	  //synchronousMethodHandlerFactory用于为每一个方法创建增强处理器
	  //我们定义的接口能够实现远程调用的秘密就在处理器里	
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy);
      //将处理器工厂封装到了ParseHandlersByName对象中
      //该对象的apply方法会为目标类的各个需要代理的方法创建一个SynchronousMethodHandler并返回一个map集合        
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      //最终将handlersByName给了ReflectiveFeign
      //第二个参数invocationHandlerFactory就是动态代理需要的处理器  马上就会被用到了    
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
  }
}

看下build方法
1 创建了SynchronousMethodHandler.Factory类,这个工厂类用于构建SynchronousMethodHandler对象。该工厂可以基于目标类的一个方法构建出一个SynchronousMethodHandler。代理类能够具备远程请求能力都靠SynchronousMethodHandler,这个后续在说
2 创建ParseHandlersByName类,内部封装了SynchronousMethodHandler.Factory。该类对SynchronousMethodHandler.Factory的能力进行了封装。该类可以为一个目标类创建一组SynchronousMethodHandler,这组数据会放到一个map中
3 最终构建ReflectiveFeign对象,构造器第一个参数是ParseHandlersByName,第二个参数是InvocationHandlerFactory.Default() 记住这两参数后续会用到

通过分析可以知道newInstance是由ReflectiveFeign对象完成的

3.6 ReflectiveFeign

public class ReflectiveFeign extends Feign {

	ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
      QueryMapEncoder queryMapEncoder) {
	    this.targetToHandlersByName = targetToHandlersByName;
	    this.factory = factory;
	    this.queryMapEncoder = queryMapEncoder;
    }	

    //代理类在这里被创建
	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()) {
	      //如果是Object的方法不处理
	      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
	        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
	      }
	    }
	    //构建动态代理处理器 基于methodToHandler
	    //factory看构造器结合上边的内容就可以知道是InvocationHandlerFactory.Default()
	    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;
	}
}

总结下newInstance方法
1 基于构造器的第一个参数对目标类创建了一组SynchronousMethodHandler,一个key是字符串 value是SynchronousMethodHandler的map
2 将第一步的结果封装到另一个map key是方法,value是SynchronousMethodHandler
3 基于构造器的第二个参数创建InvocationHandler对象,还记得第二个参数是InvocationHandlerFactory.Default()吗?
4 最后看到了我们熟悉的动态代理。这里将第二步中封装好的map放到了InvocationHandler里的dispatch熟悉中备用,记住他后续会用到哦
到这里动态代理的对象的创建位置已经找到了

四、代理对象对远程调用的实现

基于动态代理可以知道代理对象的处理逻辑就在InvocationHandler的invoke方法中。这里InvocationHandler是通过factory.create创建的。二这个factory其实就是InvocationHandlerFactory.Default()。所以跟下InvocationHandlerFactory.Default类

4.1 InvocationHandlerFactory.Default

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  /**
   * Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a
   * single method.
   */
  interface MethodHandler {
    Object invoke(Object[] argv) throws Throwable;
  }
  //在这里	
  static final class Default implements InvocationHandlerFactory {
    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      //跟这里 发现处理器是ReflectiveFeign.FeignInvocationHandler
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }
}

这里可以看到处理器就是ReflectiveFeign.FeignInvocationHandler,那么接下来就看其invoke方法

4.2 ReflectiveFeign.FeignInvocationHandler

static class FeignInvocationHandler implements InvocationHandler {
    //目标类的封装HardCodedTarget 从factorybean一直传到了这里
	private final Target target;
	//之前构建的方法处理器的map集合
    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);
    }
	
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }
      //这里就是通过方法找到方法处理器并调用其invoke方法
      //dispatch.get(method)结果就是SynchronousMethodHandler
      return dispatch.get(method).invoke(args);
    }	
}

分析invoke方法,其中dispatch就是构建代理对象时传入handler中的map,最终就是从dispatch中通过method对象拿到对应的SynchronousMethodHandler,然后在执行SynchronousMethodHandler的invoke方法。

4.3 SynchronousMethodHandler

final class SynchronousMethodHandler implements MethodHandler {
     public Object invoke(Object[] argv) throws Throwable {
        //创建模板,这里的buildTemplateFromArgs就是BuildFormEncodedTemplateFromArgs
        //RequestTemplate是一个http请求生成器 最终可以生成http请求 里边有url啥的
        //options http请求的配置项 还记得配置优先级里提到的这个对象吗
	    RequestTemplate template = buildTemplateFromArgs.create(argv);
	    Options options = findOptions(argv);
	    Retryer retryer = this.retryer.clone();
	    while (true) {
	      try {
	        //执行跟这里
	        return executeAndDecode(template, options);
	      } catch (RetryableException e) {
	        try {
	          retryer.continueOrPropagate(e);
	        } catch (RetryableException th) {
	          Throwable cause = th.getCause();
	          if (propagationPolicy == UNWRAP && cause != null) {
	            throw cause;
	          } else {
	            throw th;
	          }
	        }
	        if (logLevel != Logger.Level.NONE) {
	          logger.logRetry(metadata.configKey(), logLevel);
	        }
	        continue;
	      }
	    }
 	 }


 Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      ...}
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

    boolean shouldClose = true;
    try {
      if (logLevel != Logger.Level.NONE) {
        response =
            logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
      }
      if (Response.class == metadata.returnType()) {
         ...}
      if (response.status() >= 200 && response.status() < 300) {
        if (void.class == metadata.returnType()) {
          return null;
        } else {
          //还记得子容器里注册的那个解码器吗 继续跟这个方法 可以跟到MappingJackson2HttpMessageConverter 和mvc的解码流程一样
          Object result = decode(response);
          shouldClose = closeAfterDecode;
          return result;
        }
      } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
         ...} catch (IOException e) {
       ...}

invoke方法主要做了3件事
1 构建RequestTemplate,该对象可以生成一个请求对象 后边会用到
2 获取请求配置Options
3 执行executeAndDecode方法,这里委托给了client来执行,而这个client其实就是LoadBalancerFeignClient,入口一中注册的

4.4 LoadBalancerFeignClient

public class LoadBalancerFeignClient implements Client {
	//默认的请求参数 第一个入口中提到过哦
	static final Request.Options DEFAULT_OPTIONS = new Request.Options();

	//记住构造器的第一个参数 被封装到了这个对象里
	private final Client delegate;
	public LoadBalancerFeignClient(Client delegate,
		CachingSpringLoadBalancerFactory lbClientFactory,
		SpringClientFactory clientFactory) {
		this.delegate = delegate;
		this.lbClientFactory = lbClientFactory;
		this.clientFactory = clientFactory;
    }
	//跟这个方法
	public Response execute(Request request, Request.Options options) throws IOException {
	    
		try {
		    //获取url
			URI asUri = URI.create(request.url());
			//获取服务名称 例如【dvs-auth】
			String clientName = asUri.getHost();
			//这里把url中的服务名称清除了
			URI uriWithoutHost = cleanUrl(request.url(), clientName);
			//构建ribbon请求对象
			//这里将this.delegate传入了 封装到了其client属性中 最最最后边会用到
			FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
					this.delegate, request, uriWithoutHost);
			//获取请求配置信息 请求时长和响应时长
			IClientConfig requestConfig = getClientConfig(options, clientName);
			//这个方法简单介绍下 详细说明会涉及到大量ribbon内容 还没搞明白
			//lbClient(clientName)会返回一个feign中的FeignLoadBalancer对象 该对象继承了ribbon中的AbstractLoadBalancerAwareClient对象
			//AbstractLoadBalancerAwareClient是ribbon提供的一个负载均衡器 只要继承他 就能调用现成的负载均衡api【executeWithLoadBalancer】
			//后边需要稍微跟下AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法
			return lbClient(clientName)
					.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
		}
		catch (ClientException e) {
			IOException io = findIOException(e);
			if (io != null) {
				throw io;
			}
			throw new RuntimeException(e);
		}
	}

	//获取请求配置信息 请求时长和响应时长
	IClientConfig getClientConfig(Request.Options options, String clientName) {
		IClientConfig requestConfig;
		//如果默认没修改过 那么以ribbon的配置为准
		if (options == DEFAULT_OPTIONS) {
			requestConfig = this.clientFactory.getClientConfig(clientName);
		}
		//如果修改过 以feign为准
		else {
			requestConfig = new FeignOptionsClientConfig(options);
		}
		return requestConfig;
	}
	//这里最终返回了FeignLoadBalancer 这里不深入跟进了 知道返回了FeignLoadBalancer即可
	private FeignLoadBalancer lbClient(String clientName) {
		return this.lbClientFactory.create(clientName);
	}
}

首先这个类是在第一个入口中被注册的,当时说要重点关注构造器第一个参数Client.Default,这里可以看到被封装到了delegate属性中。
为什么要关注这个参数,因为这里边封装了对http请求的实现。后续过程中这个参数在feign和ribbon中来回传,所以需要重点关注下

继续上边的介绍在来关注execute方法,
1 创建URI对象,可以理解为url。此时的url还不是完整的因为内部还是服务名的形式【例如 http://clientname/getxxx】
2 构建FeignLoadBalancer.RibbonRequest对象 这里把Client.Default传入构造函数的第一个参数 重点关注
简单看下RibbonRequest的构造器

protected RibbonRequest(Client client, Request request, URI uri) {
    //放到了这里哦
	this.client = client;
	setUri(uri);
	this.request = toRequest(request);
}

3 lbClient(clientName)方法会返回一个FeignLoadBalancer对象,该对象实现了ribbon提供的一个基类并获得了调用负载均衡api的能力
4 最后是executeWithLoadBalancer方法,这个方法就是调用了ribbon的负载均衡api。该方法是由AbstractLoadBalancerAwareClient类完成实现的

4.5 AbstractLoadBalancerAwareClient

//可以看到 这里已经不是feign的包了
package com.netflix.client;
public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {
    //这里的requestConfig请求配置是我们上边封好传进来的 之后会把他交给子类FeignLoadBalancer
	public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
	    //构建一个指令
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
        try {
            //执行指令
            return command.submit(
                new ServerOperation<T>() {
                    @Override
                    //指令执行会触发这里的回调方法
                    //这里的server已经是负载均衡后的了,其内部是通过服务名加某种算法【默认是轮询】在服务明对应的若干负载服务中取一个
                    //Server 这里包含了ip和端口
                    public Observable<T> call(Server server) {
                        //将url中的服务名换成真正的ip和端口
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            //这里的AbstractLoadBalancerAwareClient.this其实就是FeignLoadBalancer 所以继续跟FeignLoadBalancer的execute
                            //所以这里调用了FeignLoadBalancer的execute并把具有完整请求路径的requestForServer和请求配置作为入参
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
           。。。略
        }
        
    }
}

这里很多内容涉及到了ribbon的实现,暂不深入了。这里对executeWithLoadBalancer方法关键步骤做一个简介
1 构建一个指令
2 提交指令,提交指令后ribbon会做负载均衡处理并执行call回调函数,在回调函数中Server类就是负载均衡策略选取的服务,内部包含服务真实的ip和端口。具体ribbon怎么做的这里不在跟进了
3 基于Server对象拼接出最终的可用的url
4 requestForServer就是刚刚提到的FeignLoadBalancer.RibbonRequest ,替换其url为最终的url
5 然后在执行FeignLoadBalancer的execute方法,完成最终的http请求。这里可以发现ribbon只负责了负载均衡,最终的http请求还是交给了feign来完成

4.6 FeignLoadBalancer

package org.springframework.cloud.openfeign.ribbon;
public class FeignLoadBalancer extends
		AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
	
	//到这里我们已经有了完整的url 就差发送http请求了
	public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
			throws IOException {
		Request.Options options;
		//configOverride可能不是空了 所以会走这个分支
		if (configOverride != null) {
			RibbonProperties override = RibbonProperties.from(configOverride);
			options = new Request.Options(override.connectTimeout(this.connectTimeout),
					override.readTimeout(this.readTimeout));
		}
		else {
		    //这里是ribbon的默认配置 
			options = new Request.Options(this.connectTimeout, this.readTimeout);
		}
		//这里要执行真正的http请求了
		//还记得request.client()是谁吗 就是最最最开头说的Client.Default
		Response response = request.client().execute(request.toRequest(), options);
		return new RibbonResponse(request.getUri(), response);
	}
}

主要看request.client(),这个就是在feign和ribbon中传来传去的Client.Default
继续跟进Client.Default的execute方法

4.7 Client.Default

class Default implements Client {
	 public Response execute(Request request, Options options) throws IOException {
	      //这个对象是jdk自带的http请求连接对象
	      HttpURLConnection connection = convertAndSend(request, options);
	      //发送http请求
	      return convertResponse(connection, request);
    }
}

跟到这里看到其最终调用jdk的工具类完成了http请求的执行

总结

从feign的两个入口,到动态代理对象的创建与执行,到对ribbon负载均衡的引用,最后到http请求的发送。整体做了一个总结

你可能感兴趣的:(学习,spring,java)