Feign源码解析
在使用Feign接口的时候我们首先要添加@EnableFeignClient注解,注解@EnableFeignClient导入了类FeignClientsRegistrar,如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {}
类FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,所以在Spring实例化的过程中会回调registerBeanDefinitions()方法,此方法会注册相关的BeanDefinition,基本上所有的注解都是这样的一个套路。从源码可以看到registerBeanDefinitions()先是从basePackages中扫描标记了@FeignClient的接口,如下:
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 省略for (String basePackage : basePackages) {
Set candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = https://www.it610.com/article/beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");
Map attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
然后针对扫描到的每一个@FeignClient接口调用了registerFeignClient()方法,此方法为Feign接口注册了FeignClientFactoryBean类的BeanDefinition,如下:
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map attributes) {
String className = annotationMetadata.getClassName();
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";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary");
// has a default, won't be nullbeanDefinition.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的源码可以发现FeignClientFactoryBean实现了FactoryBean接口,FactoryBean接口是一个可以自定义实例化对象的一个接口,至此,我们已经知道了在实例化Feign接口的时候会调用FeignClientFactoryBean的getObject()方法返回对象实例。所以就重点看下FeignClientFactoryBean的getObject()方法是如何返回一个Feign接口实例的。其相关源码如下:
protected T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
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-netflix-ribbon?");
}@Override
public Object getObject() throws Exception {
return getTarget();
}/**
* @param the target type of the Feign client
* @return a {@link Feign} client created with the specified data and the context information
*/
T getTarget() {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, 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();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
可以看到getObject()方法直接调用target()方法,getTarget()方法获取了FeignContext、Feign.Builder、new HardCodedTarget实例,然后调用loadBalance()方法,此方法会从Spring上下文中找Client和Targeter并调用Targeter的target()方法生成代理对象,Targeter的实现类有两个:DefaultTargeter和HystrixTargeter。DefaultTargeter的target()方法直接调用Feign.Builder的target()方法返回对象实例。HystrixTargeter的target()方法会根据Feign接口配置以及Feign.Builder的类型来决定调用DefaultTargeter的target()还是HystrixTargeter的target()返代理对象。在引入Hystrix相关依赖后就会使用HystrixTargeter,如下为HystrixTargeter的target()方法源码:
@Override
public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder, fallback);
}
Class> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
}return feign.target(target);
}
因为引入了Hystrix相关依赖所以这里的Feign.Builder类型为HystrixFeign.Builder,HystrixFeign.Builder的target()方法创建了匿名InvocationHandlerFactory对象和ReflectiveFeign对象并调用ReflectiveFeign的newInstance()方法返回代理对象,匿名InvocationHandlerFactory对象的create()方法返回了类HystrixInvocationHandler的对象实例,如下为HystrixFeign.Builder类的源码片段:
public T target(Target target, FallbackFactory extends T> fallbackFactory) {
return build(fallbackFactory).newInstance(target);
}Feign build(final FallbackFactory> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map dispatch) {
return new HystrixInvocationHandler(target, dispatch, setterFactory,
nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}
上面代码中super.build()返回的即为ReflectiveFeign对象,如下:
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的newInstance()方法首先会分析Feign接口中的方法并生成MethodHandler对象,接着调用InvocationHandlerFactory的create()方法创建InvocationHandler并使用JDK的代理方法生成代理对象,如下:
public T newInstance(Target target) {
Map nameToHandler = targetToHandlersByName.apply(target);
Map methodToHandler = new LinkedHashMap();
List defaultMethodHandlers = new LinkedList();
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);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
此处的InvocationHandler即为上面提到的匿名InvocationHandlerFactory对象的create()方法返回的HystrixInvocationHandler的对象实例,HystrixInvocationHandler为熔断器Hystrix的InvocationHandler实现,其invoke()方法引入了Hystrix的熔断模块。当没有引入Hystrix当相关依赖时InvocationHandlerFactory当实现为默认实现InvocationHandlerFactory.Default,此实现的create()方法返回的InvocationHandler为ReflectiveFeign.FeignInvocationHandler,源码如下:
public interface InvocationHandlerFactory {InvocationHandler create(Target target, Map dispatch);
static final class Default implements InvocationHandlerFactory {@Override
public InvocationHandler create(Target target, Map dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}
}
类ReflectiveFeign.FeignInvocationHandler的属性dispatch保存了Method到MethodHandler
的映射,invoke()方法会根据Method找到MethodHandler并执行其invoke()方法,源码如下:
@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);
}
MethodHandler的具体实现为SynchronousMethodHandler,此类的invoke()方法创建RequestTemplate对象并交由Client执行,调用Client的execute()方法发起Http请求并返回Response对象,完成一个请求的调用,如下:
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);
} 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) 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);
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
// 略
}
Client的实现有两个:Client.Default和LoadBalancerFeignClient,前者使用HttpURLConnection发起Http请求,后者调用FeignLoadBalancer的executeWithLoadBalancer方法使用Ribbon负载均衡发起Http请求。Client.Default源码如下:
package feign;
public interface Client {
Response execute(Request request, Options options) throws IOException;
public static class Default implements Client {// 省略@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
final HttpURLConnection connection =
(HttpURLConnection) new URL(request.url()).openConnection();
if (connection instanceof HttpsURLConnection) {
HttpsURLConnection sslCon = (HttpsURLConnection) connection;
if (sslContextFactory != null) {
sslCon.setSSLSocketFactory(sslContextFactory);
}
if (hostnameVerifier != null) {
sslCon.setHostnameVerifier(hostnameVerifier);
}
}
// 略
return connection;
}Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
int status = connection.getResponseCode();
String reason = connection.getResponseMessage();
// 略
return Response.builder()
.status(status)
.reason(reason)
.headers(headers)
.request(request)
.body(stream, length)
.build();
}
}
}
LoadBalancerFeignClient类相关源码:
public class LoadBalancerFeignClient implements Client {// 略@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}private FeignLoadBalancer lbClient(String clientName) {
return this.lbClientFactory.create(clientName);
}// 略
}
总结
Feign接口运行过程大致分为如下几个步骤:
- @EnableFeignClient注解导入了FeignClientsRegistrar类。
- 执行FeignClientsRegistrar的registerBeanDefinitions()方法扫描basePackages中的Feign接口并为每个Feign接口注册FeignClientFactoryBean的BeanDefinition。
- Spring在实例化Feign接口时调用FeignClientFactoryBean类的getObject()方法返回Feign接口的代理实例。
- 创建InvocationHandler对象并使用JDK代理生成代理类。
- 如果引入了Hystrix的相关依赖则引入Hystrix的熔断模块(HystrixInvocationHandler)。
- 如果引入了Ribbon的相关依赖则Client接口会引入Ribbon的负载均衡模块(LoadBalancerFeignClient)。
- 发起Http调用,返回结果。
文章图片
image
推荐阅读
- Android事件传递源码分析
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- Java内存泄漏分析系列之二(jstack生成的Thread|Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析)
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Android系统启动之init.rc文件解析过程
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- 小程序有哪些低成本获客手段——案例解析
- Vue源码分析—响应式原理(二)
- SwiftUI|SwiftUI iOS 瀑布流组件之仿CollectionView不规则图文混合(教程含源码)