Feign原理 - 源码解析
本文基于SpringCloud版本:Hoxton.SR12(2.2.9.RELEASE)
一、扫描FeignClient生成代理Bean放入Spring容器
1.@EnableFeignClient注解导入注册类。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
}
@Import注解用于注册Bean到容器,FeignClientsRegistrar类实现ImportBeanDefinitionRegistrar接口。
ImportBeanDefinitionRegistrar接口中的registerBeanDefinitions方法负责注册Bean。
进入FeignClientsRegistrar类registerBeanDefinitions方法:
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
this.registerDefaultConfiguration(metadata, registry);
this.registerFeignClients(metadata, registry);
}
其中this.registerDefaultConfiguration(metadata, registry)负责注册@EnableFeignClients注解指定defaultConfiguration的配置类到容器。
进入this.registerFeignClients(metadata, registry)方法:
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//存放需要注册到容器bean对应的BeanDefinition
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
//@EnableFeignClients注解中指定的值
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//@EnableFeignClients注解中指定的clients
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
//@EnableFeignClients注解没有指定clients属性的情况下
if (clients == null || clients.length == 0) {
//获取候选Bean扫描器,该扫描器只扫描非注解类并且能够实例化的类
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
//指定扫描带有@FeignClient注解的类
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
/** 获取@EnableFeignClients注解中value、basePackages、basePackageClasses属性指定 ** 的包作为扫描路径,若value、basePackages、basePackageClasses都没有指定,则扫描路径 ** 默认为@EnableFeignClients所在包的路径**/
Set<String> basePackages = getBasePackages(metadata);
//用扫描器扫描指定的路径,筛选出符合注册条件的Bean
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
//@EnableFeignClients注解指定clients属性的情况下
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
//验证@FeignClient注解是否标注在接口上
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());
//决定Bean name
String name = getClientName(attributes);
//注册@Feign注解中属性configuration指定的配置类到容器中
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注册@Fegin注解标注的类的容器
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
进入registerFeignClient(registry, annotationMetadata, attributes)方法:
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对象
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
//为创建FeignClientFactoryBean对象设置参数
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
//根据FeignClientFactoryBean对象构造BeanDefinition
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);
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[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
qualifiers);
//将FeignClientFactoryBean对象对应的BeanDefinition转化为Bean注册到容器
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
在SpringIOC源码中,从Spring容器中获取Bean都是根据对应的FactoryBean接口中getObject()方法获取。
FeignClientFactoryBean实现了FactoryBean接口,即每次从Spring容器中获取FeignClient都是根据getObject方法获取。
进入FeignClientFactoryBean#getObject:
@Override
public Object getObject() {
return getTarget();
}
一直深入内部直到ReflectiveFeign#newInstance方法:
public <T> T newInstance(Target<T> target) {
//Feign接口中的方法与方法处理器映射
Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
Method[] var5 = target.type().getMethods();
int var6 = var5.length;
//为nameToHandler赋值
for(int var7 = 0; var7 < var6; ++var7) {
Method method = var5[var7];
if (method.getDeclaringClass() != Object.class) {
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)));
}
}
}
//生成JDK代理需要的InvocationHandler,顺便把methodToHandler存放在代理处理类,用于根据方法 //获取对应的方法处理器
InvocationHandler handler = this.factory.create(target, methodToHandler);、
//创建代理对象
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
Iterator var12 = defaultMethodHandlers.iterator();
while(var12.hasNext()) {
DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
二、Feign对象的代理逻辑
众所周知,执行代理对象的方法时实际上是执行InvocationHandler#invoke方法。
FeignInvocationHandler#invoke方法逻辑为:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!"equals".equals(method.getName())) {
if ("hashCode".equals(method.getName())) {
return this.hashCode();
} else {
//不是equals、hashCode、toString方法时,根据方法获取对应的方法处理器,并执行 //invoke方法
return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
}
} else {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return this.equals(otherHandler);
} catch (IllegalArgumentException var5) {
return false;
}
}
}
其中MethodHandler接口的实现类为SynchronousMethodHandler,其invoke方法即为调用feign接口进行远程调用时实际执行的逻辑。
三、Feign远程调用时执行的逻辑
执行流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NXcVLleu-1628600042181)(./feign执行流程.png)]
将断点设置在调用feign方法中,在IDEA中按F7由debug进入SynchronousMethodHandler#invoke方法:
public Object invoke(Object[] argv) throws Throwable {
//生成RequestTemplate
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
//获取配置参数
Options options = this.findOptions(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
//执行远程调用并将响应结果解码。
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {
RetryableException e = var9;
try {
//失败了没关系,重试。
retryer.continueOrPropagate(e);
} catch (RetryableException var8) {
Throwable cause = var8.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var8;
}
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
其中RequestTemplate中已填充的参数有:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7dY3kikV-1628600042186)(./requestTemplate.png)]
获取Options参数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysdTawqM-1628600042188)(./options参数.png)]
进入执行远程调用并将响应结果解码方法executeAndDecode:
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//执行RequestInterceptor拦截器
Request request = this.targetRequest(template);
if (this.logLevel != Level.NONE) {
this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
}
long start = System.nanoTime();
Response response;
try {
//远程调用
response = this.client.execute(request, options);
//设置request和template在响应结果中。
response = response.toBuilder().request(request).requestTemplate(template).build();
} catch (IOException var12) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var12, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var12);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (this.decoder != null) {
//默认使用SpringDecoder解码器,里面用到HttpMessageConvertor,即SpringMVC能转换的响应结果这里也能转换
return this.decoder.decode(response, this.metadata.returnType());
} else {
CompletableFuture<Object> resultFuture = new CompletableFuture();
this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone()) {
throw new IllegalStateException("Response handling not done");
} else {
return resultFuture.join();
}
} catch (CompletionException var13) {
Throwable cause = var13.getCause();
if (cause != null) {
throw cause;
} else {
throw var13;
}
}
}
}
其中进行远程调用的Client有:
(1)Client.Default类:默认的feign.Client 客户端实现类,内部使用HttpURLConnnection 完成URL请求处理;
在JKD1.8中,虽然在HttpURLConnnection 底层,使用了非常简单的HTTP连接池技术,但是,其HTTP连接的复用能力,实际是非常弱的,性能当然也很低。
(2)ApacheHttpClient 类:内部使用 Apache httpclient 开源组件完成URL请求处理的feign.Client 客户端实现类;
(3)OkHttpClient类:内部使用 OkHttp3 开源组件完成URL请求处理的feign.Client 客户端实现类。
(4)LoadBalancerFeignClient 类:内部使用 Ribben 负载均衡技术完成URL请求处理的feign.Client 客户端实现类。
参考文献:
1.https://www.cnblogs.com/crazymakercircle/p/11965726.html
2.https://docs.spring.io/spring-cloud-openfeign/docs/2.2.9.RELEASE/reference/html/