版本
Version: spring-cloud-openfeign-core:3.0.1
Spring Cloud openfeign 使用
我们首先看看Spring Cloud openfeign 是如何使用的,然后基于使用进行一个源码分析
-
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<!-- <version>3.0.1</version>-->
</dependency>
-
开启Feign
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
-
接口调用
@FeignClient(name = "test", url = "127.0.0.1:10080")
public interface PlutusClient {
@RequestMapping(method = RequestMethod.GET, value ="/test/v1/testRpc")
String testRpc();
}
源码分析
1. 开启Feign自动装配注解 @EnableFeignClients
-
@EnableFeignClients.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
//等价于basePackages属性,更简洁的方式
String[] value() default {};
//指定多个包名进行扫描
String[] basePackages() default {};
//指定多个类或接口的class,扫描时会在这些指定的类和接口所属的包进行扫描
Class<?>[] basePackageClasses() default {};
//为所有的Feign Client设置默认配置类
Class<?>[] defaultConfiguration() default {};
//指定用@FeignClient注释的类列表。如果该项配置不为空,则不会进行类路径扫描
Class<?>[] clients() default {};
}
我们可以看到这个注解上面添加了@Import
注解,不知道@Import
注解的参考 我之前的博文
想写Spring Boot SDK?先深入学习下@Import 注解吧
可以看到@Import
注解导入了一个配置类FeignClientsRegistrar.class
, 所以核心处理逻辑都在这个类了
2. 配置类 FeignClientsRegistrar.class
这里不会贴所有源代码,防止大家看不懂,我们一步一步基于方法来分析,部分非主流程细节将省略
首先FeignClientsRegistrar
实现了接口ImportBeanDefinitionRegistrar
,这个接口就是spring动态注册Bean的方法, 所以我们直接看FeignClientsRegistrar
实现了ImportBeanDefinitionRegistrar
接口的registerBeanDefinitions
的方法,这个方法就是源码的入口
-
ImportBeanDefinitionRegistrar
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
-
FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
}
这里可以看到有两个方法registerDefaultConfiguration
和registerFeignClients
-
registerDefaultConfiguration
看名字就知道是注册默认的配置类,我们简单的看看吧,这个方法并不是主流程
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 获取注解 EnableFeignClients 上的值
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
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"));
}
}
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());
}
可以看看defaultAttrs的值

然后将FeignClientSpecification
注入spring容器中
-
registerFeignClients
这里是生成接口的代理类的核心逻辑,我们重点来看看
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// 获取注解 EnableFeignClients 上的值
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
// 1. 未指定EnableFeignClients注解中的clients属性,则去扫描
if (clients == null || clients.length == 0) {
// 获取 ClassPath扫描器
ClassPathScanningCandidateComponentProvider scanner = getScanner();
// 为扫描器设置资源加载器org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
scanner.setResourceLoader(this.resourceLoader);
// 添加 注解类型过滤器,只过滤@FeignClient
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
// 获取需要扫描的基础包集合
Set<String> basePackages = getBasePackages(metadata);
// 扫描基础包,且满足过滤条件下的接口封装成BeanDefinition加入集合candidateComponents中
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
// 2. 指定了EnableFeignClients注解中的clients属性
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
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");
// 4. 获取 client FeignClient注解上的属性
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
// 5. 注册配置类
registerClientConfiguration(registry, name, attributes.get("configuration"));
// 6.生成 feign client代理类
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
-
EnableFeignClients
注解中没有指定client属性就去扫描包 -
如果 EnableFeignClients
指定了client class就直接去注册这些feign client -
我们看看candidateComponents里面包含的class

可以看到扫描到了我们定义的client:PlutusClient
但是需要注意这里的class 还是只是接口,所以我们需要生成这个接口的代理类
-
遍历我们扫描到的feign Client,首先获取到 FeignClient
注解上的属性

-
通过方法 registerClientConfiguration
注册client的配置类 -
通过方法 registerFeignClient
生成feign client的代理类
我们进去这个方法看看
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
// 核心类 FeignClientFactoryBean 实现了 FactoryBean 接口
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
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
实现了FactoryBean
接口,spring中实现了FactoryBean接口的类在获取的时候都是获取的他的代理对象即getObject()
方法返回的对象,所以代理对象生成的逻辑我们直接去看getObject()
就好
@Override
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
//1. 从应用上下文中获取创建 feign 客户端的上下文对象 FeignContext
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
// 2. 构建一个 Feign.Builder 对象,最终有Builder对象来生成Feign代理对象
Feign.Builder builder = feign(context);
// FeignClient 属性 url 没有被指定
if (!StringUtils.hasText(url)) {
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
// 3. 通过loadBalance负载均衡并生成feign Client代理对象
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
// @FeignClient 属性 url 属性被指定的情况,则不需要负载均衡
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
// 因为指定了明确的服务节点url,所以这里不需要负载均衡
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
// 生成最终的feign client 实例
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
-
构建一个
FeignContext
,包含后面生成Feign需要用到的一些属性 -
构建一个
Feign.Builder
供后续创建代理对象使用protected Feign.Builder feign(FeignContext context) {
// log 相关不用管
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))//编码器
.decoder(get(context, Decoder.class))//解码器
.contract(get(context, Contract.class));// 上下文
// @formatter:on
// 设置一些客户端属性 比如feign读取超时时间、连接超时时间等
configureFeign(context, builder);
// 扩展给外部使用者,外部使用者可以给Feign.Builder设置其他参数
applyBuildCustomizers(context, builder);
return builder;
} -
通过loadBalance负载均衡并生成feign Client代理对象
我们进入loadBalance()
方法看看
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
// 1.获取从spring中获取Client实现类 FeignBlockingLoadBalancerClient
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
// 2. 获取Targeter 实现类 DefaultTargeter
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
}
我们通过debug可以看到Client
的实现类为FeignBlockingLoadBalancerClient

那么这个类在哪里创建的呢?很明显就是就自动装配配的,我们通过搜索FeignBlockingLoadBalancerClient
并查看在哪使用最终定位到在DefaultFeignLoadBalancerConfiguration
中装配的

很明显是通过springboot的spi自动装配的,这里就不分析了,我们继续回到主线
这里Targeter
获取到的实现类是DefaultTargeter
分析方式和上面类似就不展开了
最后生成代理对象的方法就是这里了
-
Feign.class
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
// 最终根据各种配置生成一个 ReflectiveFeign 对象
public Feign build() {
Client client = Capability.enrich(this.client, capabilities);
Retryer retryer = Capability.enrich(this.retryer, capabilities);
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Encoder encoder = Capability.enrich(this.encoder, capabilities);
Decoder decoder = Capability.enrich(this.decoder, capabilities);
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
// ReflectiveFeign.java 方法
// 创建最终的feign客户端实例 : 一个 ReflectiveFeign$FeignInvocationHandler 的动态代理对象
public <T> T newInstance(Target<T> target) {
// MethodHandler 接口实现类默认 √ 真正的代理逻辑实现
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)));
}
}
// factory = InvocationHandlerFactory.Default 内部类
// 这里 create 创建为 ReflectiveFeign 参考下面create方法
InvocationHandler handler = factory.create(target, methodToHandler);
// 创建feign客户端实例的动态代理对象
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
// 将缺省方法处理器绑定到feign客户端实例的动态代理对象上
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
// InvocationHandlerFactory.Default.class
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
代理类真正的逻辑实现是在SynchronousMethodHandler
方法里面
public Object invoke(Object[] argv) throws Throwable {
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 {
// 这个client是 Client.Default 去调用
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) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (decoder != null)
return decoder.decode(response, metadata.returnType());
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(),
elapsedTime);
try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");
return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
可以看到核心实现又是委托给了实际的网络调用是在Client中调用的
整体的执行流程就是这样了,大致流程源码就分析完成 了
总结
总的流程来说就是当我们使用@EnableFeignClients
注解后就启动了feign的客户端扫描注册机制,然后扫描那些被@FeignClient
装饰的接口,然后为这些接口生成动态代理类,这些代理类最终的代理实现是交给SynchronousMethodHandler
去实现,而真正实现远程调用又是交给了Client.Default
去执行网络请求
原文始发于微信公众号(小奏技术):图文Debug一步一步带你看清SpringCloud openfeign源码执行过程
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30079.html