OpenFeign源码解读

2023-05-09 16:14:16 浏览数 (2)

前言:

参考学习链接:

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实现负载均衡

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

0 人点赞