前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenFeign源码解读

OpenFeign源码解读

原创
作者头像
小爽只会CRUD
修改2023-05-09 16:14:16
8820
修改2023-05-09 16:14:16
举报
文章被收录于专栏:一头猪的学习一头猪的学习

前言:

参考学习链接:

1.https://www.bilibili.com/video/BV11D4y1C73V/?spm_id_from=333.1007.top_right_bar_window_history.content.click

2.深入理解Feign之源码解析-腾讯云开发者社区-腾讯云 (tencent.com)

3.https://github.com/OpenFeign/feign

工作原理:

openFeign是一个伪客户端(以下简称Feign),即它不做任何的请求处理。Feign通过处理注解生成request,从而实现简化HTTP API开发的目的,即开发人员可以使用注解的方式定制request api模板,在发送http request请求之前,Feign通过处理注解的方式替换掉request模板中的参数,这种实现方式显得更为直接、可理解。

问题:为何只需要定义接口而没有实现类?

拆解问题,思路分析

问题一:如何动态生成实现类?

A:动态代理(cglib 或者 jdk)

问题二:代理对象如何交给Spring容器?

A:把Bean交给Spring容器的方法有以下几种:

1.xml声明bean<bean id = "", class = "">

2.@ComponentScan + @Service/@Controller/@Repository/@Component

3.Import(XXX.class)

4.ImportSelector接口,该接口返回类名数组

5.ImportBeanDefinitionRegistart接口,该接口实现registerBeanDefinitions方法

6.@Bean注解

7.FactoryBean接口,该接口实现getObject方法

8.SingletonBeanRegistry.registerSingleton()方法

其中前5种方法bean的创建交给spring负责,流程如下:

class -> beanDefinition -> bean -> cache

那么如何把一个第三方对象,也就是完全由我们自己控制创建的对象交给Spring管理呢?

6.@Bean注解

2.FactoryBean接口,该接口实现getObject方法

3.SingletonBeanRegistry.registerSingleton()方法

问题三:多个接口需要写多个对应的FactoryBean类吗?

A:不需要,原因如下:

1.只要定义一个FactoryBean类,把接口的Class作为变量传给FactoryBean

2.针对不同的接口需要创建不同的FactoryBean对象,每个FactoryBean对象所持有的接口类型是不同的

代码语言:java
复制
class FeignClientFactoryBean implements FactoryBean<Object> {
	private Class<?> type; // 接口类型
    
    @Override
	public Object getObject() throws Exception {
        // 返回代理对象
		return Proxy.newProxyInstance(this.getClassLoader(),new Class<?>[] {type}, new InvocationHandler());
	}
}

问题四:一个FactoryBean类如何创建多个持有不同的接口类型的对象呢?

A:有两种方法

1.创建多个BeanDefinition,也就是调用BeanDefinitionBuilder.build()

2.每个BeanDefinition指定不同的接口类型

BeanDefinitionBuilder.addPropertyValue(String name, @Nullable Object value)

​BeanDefinitionBuilder.addConstructorArgValue(@Nullable Object value)

问题五:如何把自定义的BeanDefinition交给Spring?

A:实现ImportBeanDefinitionRegistrar接口

需要实现registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

@Import、ImportSelector、ImportBeanDefinitionRegistrar的使用和区别

1)@Import(XXX.class)一般配合ImportSelector或者ImportBeanDefinitionRegistrar使用

2)ImportSelector返回的是全类名数组,用于选择需要的配置类

3)ImportBeanDefinitionRegistrar提供BeanDefinitionRegistry,用于注册自定义的BeanDefinition

问题六:如何获取带有@FeignClient注解的接口和注解信息?

A:包扫描,Spring 提供ClassPathScanningCandidateComponentProvider类做包扫描功能

代码语言:java
复制
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    private final List<TypeFilter> includeFilters = new LinkedList<>();

	private final List<TypeFilter> excludeFilters = new LinkedList<>();
    
    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);
		}
	}
    
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

			for (Resource resource : resources) {
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = getMetadataReaderFactory().
                            getMetadataReader(resource);
                        // 第一次判断是否是候选组件
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition
                                (metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
                            // 第二次判断是否是候选组件
							if (isCandidateComponent(sbd)) {
								candidates.add(sbd);
							}
						}	
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}
    
    /** 用类型过滤器来判断是否是候选的组件 */
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}
    
    /** 判断bean定义是否符合候选的组件:独立的并且是具体的(不是接口或抽象类)  可以重写 */
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}
}

源码解读

@EnableFeignClients注解,关注到@Import(FeignClientsRegistrar.class)

代码语言:java
复制
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

	// basePackages的别名
	String[] value() default {};

	// 扫描的包
	String[] basePackages() default {};

	// 扫描的包的class
	Class<?>[] basePackageClasses() default {};

    // 默认的配置类
	Class<?>[] defaultConfiguration() default {};

	// 手动传入的feign client对应的Class
	Class<?>[] clients() default {};

}

FeignClientsRegistrar

代码语言:java
复制
class FeignClientsRegistrar
		implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
	// 一个入口
    @Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
        // 注册默认配置
		registerDefaultConfiguration(metadata, registry);
        // 注册feign clients 关注
		registerFeignClients(metadata, registry);
	}
    
    /** 注册默认配置的bean定义(FeignClientSpecification) */
    private void registerDefaultConfiguration(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
        // 从EnableFeignClients注解取出所有的属性值
		Map<String, Object> defaultAttrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        // 如果有配置defaultConfiguration
		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			registerClientConfiguration(registry, name,
					defaultAttrs.get("defaultConfiguration"));
		}
	} 

    
    /** 注册所有的feign client的bean定义(FeignClientFactoryBean) */
    public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
        // 获取扫描器
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);

		Set<String> basePackages;
		
		// 获取EnableFeignClients的属性转成map对象存储
		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
        
        // 创建注解类型的过滤器用于过滤出带有FeignClient注解的类或接口
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
				FeignClient.class);
		final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
		
		// 现在是通过包扫描来获取接口的,也可以直接告诉它clients有哪些(但是一般不这样做)
		if (clients == null || clients.length == 0) {
			scanner.addIncludeFilter(annotationTypeFilter);
			// 获取包
			basePackages = getBasePackages(metadata);
		}
		else {
			final Set<String> clientClasses = new HashSet<>();
			basePackages = new HashSet<>();
			for (Class<?> clazz : clients) {
				basePackages.add(ClassUtils.getPackageName(clazz));
				clientClasses.add(clazz.getCanonicalName());
			}
			AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
				@Override
				protected boolean match(ClassMetadata metadata) {
					String cleaned = metadata.getClassName().replaceAll("\\$", ".");
					return clientClasses.contains(cleaned);
				}
			};
			scanner.addIncludeFilter(
					new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
		}

        /** 进行包扫描 */
		for (String basePackage : basePackages) {
            // 根据每一个包找出候选的bean定义
			Set<BeanDefinition> candidateComponents = scanner
					.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
					AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    // 获取注解的数据
					AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
					// 获取FeignClient注解的属性值
					Map<String, Object> attributes = annotationMetadata
							.getAnnotationAttributes(
									FeignClient.class.getCanonicalName());
					// 获取FeignClient的名字
					String name = getClientName(attributes);
                    // 注册每个feign client注册对应的配置(FeignClientSpecification)
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));
					// 注册feign client的bean定义(FeignClientFactoryBean)最重要的方法
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}
    
    /** 获取扫描器 重写第二个isCandidateComponent */
    protected ClassPathScanningCandidateComponentProvider getScanner() {
		return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
			@Override
			protected boolean isCandidateComponent(
					AnnotatedBeanDefinition beanDefinition) {
				// 默认不是候选的组件
				boolean isCandidate = false;
                // 如果是独立的
				if (beanDefinition.getMetadata().isIndependent()) {
					// 如果不是注解
					if (!beanDefinition.getMetadata().isAnnotation()) {
						isCandidate = true;
					}
				}
				return isCandidate;
			}
		};
	}
    
    /** 根据配置类生成并注册FeignClientSpecification的bean定义*/
    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
			Object configuration) {
        
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
		registry.registerBeanDefinition(
				name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}
    
    /** 生成并注册FeignClientFactoryBean的bean定义 */
    private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		// 转换成BeanDefinition FeignClientFacotryBean实现了FactoryBean接口
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		validate(attributes);
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		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";
		// 创建出beanDefinition
		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);
	}
}

FeignClientFactoryBean

代码语言:java
复制
class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
    
    private Class<?> type;
    
    @Override
	public Object getObject() throws Exception {
		// 返回真正的代理对象
		return getTarget();
	}

	/**
	 * @param <T> the target type of the Feign client
	 * @return a {@link Feign} client created with the specified data and the context
	 * information
	 */
	<T> T getTarget() {
		FeignContext context = this.applicationContext.getBean(FeignContext.class);
		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));
		}
		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();
			}
			if (client instanceof FeignBlockingLoadBalancerClient) {
				// not load balancing because we have a url,
				// but Spring Cloud LoadBalancer is on the classpath, so unwrap
				client = ((FeignBlockingLoadBalancerClient) 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));
	}
}

DefaultTargeter

代码语言:java
复制
class DefaultTargeter implements Targeter {

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

}

Feign

代码语言:java
复制
public abstract class Feign {

  public static Builder builder() {
    return new Builder();
  }
    
  public <T> T target(Target<T> target) {
    return build().newInstance(target);
  }
    
  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);
  }  
}

ReflectiveFeign

代码语言:java
复制
public class ReflectiveFeign extends Feign {
    
  private final InvocationHandlerFactory factory;
  @Override
  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);
    // jdk动态代理 主要是第三个参数
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }
}

总结:

只需要定义接口和注解,不需要具体的实现类

方案:根据接口动态生成代理对象,把增强功能封装在里面,然后交给Spring管理

技术点:动态代理,FactoryBean接口,包扫描,如何把自定义的Bean 定义交给spring(ImportBeanDefinitionRegistrar), 如何把自定义的对象交给spring

如何发送http请求?

如何组件化?

定义接口,该接口用来发送请求和响应请求

代码语言:java
复制
public interface Client {
	Response execute(Request request, Options options) throws IOException;
}

已有方案有哪些?

1.rest template

2.http client

3.okhttp

如何整合已有的方案?

利用适配器模式

HttpClientAdapter里的代理对象是已有方案HttpClient,在真正调用Client的excute方法的时候,实际调用的是HttpClient的excute

代码语言:java
复制
/** http client的适配器 */
public final class ApacheHttpClient implements Client {
 
  private final HttpClient client;

  public ApacheHttpClient(HttpClient client) {
    this.client = client;
  }

  @Override
  public Response execute(Request request, Request.Options options) throws IOException {
    HttpUriRequest httpUriRequest;
    try {
      // 在这里进行了转换openfeign的->http的
      httpUriRequest = toHttpUriRequest(request, options);
    } catch (URISyntaxException e) {
      throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);
    }
    // http的->openfeign
    HttpResponse httpResponse = client.execute(httpUriRequest);
    return toFeignResponse(httpResponse, request);
  }
}

/** ok http 的适配器 */
public final class OkHttpClient implements Client {
    
  private final okhttp3.OkHttpClient delegate;

  public OkHttpClient(okhttp3.OkHttpClient delegate) {
    this.delegate = delegate;
  }

 @Override
  public feign.Response execute(feign.Request input, feign.Request.Options options)
      throws IOException {
    okhttp3.OkHttpClient requestScoped;
    if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
        || delegate.readTimeoutMillis() != options.readTimeoutMillis()) {
      requestScoped = delegate.newBuilder()
          .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
          .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
          .followRedirects(options.isFollowRedirects())
          .build();
    } else {
      requestScoped = delegate;
    }
    Request request = toOkHttpRequest(input);
    Response response = requestScoped.newCall(request).execute();
    return toFeignResponse(response, input).toBuilder().request(input).build();
  }
}

注意需要引入以下依赖

代码语言:html
复制
<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-httpclient</artifactId>
</dependency>
<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-okhttp</artifactId>
</dependency>

如何动态选择实现方案?

插拔式

1.Java SPI -> 无法提供依赖注入,无法动态选择实现类

2.Dubbo SPI -> 额外添加Dubbo依赖,Dubbo SPI与业务模型耦合

3.SpringBoot的自动装配 -> OpenFeign作为SpringCloud组件之一直接依托于SpringBoot

tips:找自动装配类的技巧:

1.Ctrl+G -> find Usages 功能 寻找new Instance

2.通过名字去猜 autoconfiguration结尾, 其中带有feign开头

3.直接通过 spring.factories 文件去搜索

feign带负载均衡的自动装配类

代码语言:java
复制
// 按照import的顺序 httpclient -> okhttp -> jdk
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
		OkHttpFeignLoadBalancedConfiguration.class,
		DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
	// ...
}

HttpClient适配器的配置类

代码语言:java
复制
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancedConfiguration {

	@Bean
	@ConditionalOnMissingBean(Client.class)
	public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
			SpringClientFactory clientFactory, HttpClient httpClient) {
		ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
		return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
	}
}

HttpClient的配置类

代码语言:java
复制
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CloseableHttpClient.class)
public class HttpClientFeignConfiguration {

	private final Timer connectionManagerTimer = new Timer(
			"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);

	private CloseableHttpClient httpClient;

	@Autowired(required = false)
	private RegistryBuilder registryBuilder;

	@Bean
	@ConditionalOnMissingBean(HttpClientConnectionManager.class)
	public HttpClientConnectionManager connectionManager(
			ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
			FeignHttpClientProperties httpClientProperties) {
		final HttpClientConnectionManager connectionManager = connectionManagerFactory
				.newConnectionManager(httpClientProperties.isDisableSslValidation(),
						httpClientProperties.getMaxConnections(),
						httpClientProperties.getMaxConnectionsPerRoute(),
						httpClientProperties.getTimeToLive(),
						httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
		this.connectionManagerTimer.schedule(new TimerTask() {
			@Override
			public void run() {
				connectionManager.closeExpiredConnections();
			}
		}, 30000, httpClientProperties.getConnectionTimerRepeat());
		return connectionManager;
	}

    // ...

	@Bean
	@ConditionalOnProperty(value = "feign.compression.response.enabled",
			havingValue = "false", matchIfMissing = true)
	public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
			HttpClientConnectionManager httpClientConnectionManager,
			FeignHttpClientProperties httpClientProperties) {
		this.httpClient = createClient(httpClientFactory.createBuilder(),
				httpClientConnectionManager, httpClientProperties);
		return this.httpClient;
	}

	private CloseableHttpClient createClient(HttpClientBuilder builder,
			HttpClientConnectionManager httpClientConnectionManager,
			FeignHttpClientProperties httpClientProperties) {
		RequestConfig defaultRequestConfig = RequestConfig.custom()
				.setConnectTimeout(httpClientProperties.getConnectionTimeout())
				.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
		CloseableHttpClient httpClient = builder
				.setDefaultRequestConfig(defaultRequestConfig)
				.setConnectionManager(httpClientConnectionManager).build();
		return httpClient;
	}
}

如何装配组件?

A:SynchronousMethodHandler

代码语言:java
复制
public class ReflectiveFeign extends Feign {
    // ...
    
    /** 创建JDK动态代理对象 */
    @Override
    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)));
          }
        }
        // 通过工厂创建FeignInvocationHandler对象并把methodToHandler封装进去
        InvocationHandler handler = factory.create(target, methodToHandler);
        // JDK动态代理的API
        T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
            new Class<?>[] {target.type()}, handler);

        for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
          defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
    }
    
    // jdk动态代理的第三个参数InvocationHandler
    static class FeignInvocationHandler implements InvocationHandler {
        private final Target target;
        private final Map<Method, MethodHandler> dispatch; // 每个方法封装到MethodHandler

        FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
          this.target = checkNotNull(target, "target");
          this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
        }

        @Override
        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();
          }

          return dispatch.get(method).invoke(args);
        }
    }
}
代码语言:java
复制
final class SynchronousMethodHandler implements MethodHandler {

  private final MethodMetadata metadata;
  private final Target<?> target;
  private final Client client; // http 请求客户端
  private final Retryer retryer;
  private final List<RequestInterceptor> requestInterceptors;
  private final Logger logger;
  private final Logger.Level logLevel;
  private final RequestTemplate.Factory buildTemplateFromArgs;
  private final Options options;
  // ...
    
  private SynchronousMethodHandler(Target<?> target, Client client, Retryer retryer,
      List<RequestInterceptor> requestInterceptors, Logger logger,
      Logger.Level logLevel, MethodMetadata metadata,
      RequestTemplate.Factory buildTemplateFromArgs, Options options,
      Decoder decoder, ErrorDecoder errorDecoder, boolean decode404,
      boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy) {
    // ...
    this.client = checkNotNull(client, "client for %s", target);
    // ...
  }
    
  /** 真正地调用每个方法 */  
  @Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        // 调用client
        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;
      }
    }
  }
    
  /** 该方法是通RequestTemplate生成Request请求对象 然后根据用client获取response*/
  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 {
      // 响应对象 调用client组件的execute方法
      response = client.execute(request, options); 
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    // ...
  }  
}

如何获取组件?

1.Autowired自动装配

2.获取BeanFactory或ApplicatonContext

顶级Parent Application context -> FeignContext -> FeignContext -> Map<> contexts -> 包含了子模块Service Application context

如何传递组件?

Feign.builder() 传给 SynchronousMethodHandler.Factory -> SynchronousMethodHandler

总结:

设计:组件化思维

技术点:适配器模式,SpringBoot自动装配,父子容器

OpenFeign的源码实现过程如下:

  • 通过@EnableFeignClients注解开启FeignClient
  • 根据Feign的规则实现接口,加上FeignClient注解
  • Application程序启动进行包扫描,扫描所有@FeignClient注解的类,把这些信息注入ioc容器
  • 当接口的方法被调用通过jdk动态代理,生成具体的RequesTemplate
  • RequesTemplate再生成Request对象
  • Request对象交给Client组件处理
  • Client组件封装到LoadBalanceClient类,该类结合Ribbon实现负载均衡

以上内容仅为个人学习所用,如有侵权请联系!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 工作原理:
  • 问题:为何只需要定义接口而没有实现类?
    • 问题一:如何动态生成实现类?
      • 问题二:代理对象如何交给Spring容器?
        • 问题三:多个接口需要写多个对应的FactoryBean类吗?
          • 问题四:一个FactoryBean类如何创建多个持有不同的接口类型的对象呢?
            • 问题五:如何把自定义的BeanDefinition交给Spring?
              • 问题六:如何获取带有@FeignClient注解的接口和注解信息?
              • 源码解读
              • 总结:
              • 如何发送http请求?
                • 如何组件化?
                  • 已有方案有哪些?
                    • 如何整合已有的方案?
                      • 如何动态选择实现方案?
                        • tips:找自动装配类的技巧:
                          • 如何装配组件?
                            • 如何获取组件?
                              • 如何传递组件?
                              • 总结:
                              • OpenFeign的源码实现过程如下:
                              相关产品与服务
                              容器服务
                              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档