feign是一个伪客户端,即它不做任何的请求处理。Feign通过处理注解生成request,从而实现简化HTTP API开发的目的,即开发人员可以使用注解的方式定制request api模板,在发送http request请求之前,feign通过处理注解的方式替换掉request模板中的参数,这种实现方式显得更为直接、可理解。
启动配置上检查是否有@EnableFeignClients注解,如果有该注解,则开启包扫描,扫描被@FeignClient注解修饰的接口。扫描出该注解后,通过beanDefinition注入到IOC容器中,方便后续被调用使用。
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
scanner.setResourceLoader(this.resourceLoader);
Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
......
//遍历该项目所需调用的服务
Iterator var17 = ((Set)basePackages).iterator();
while(var17.hasNext()) {
String basePackage = (String)var17.next();
Set candidateComponents = scanner.findCandidateComponents(basePackage);
Iterator var21 = candidateComponents.iterator();
while(var21.hasNext()) {
BeanDefinition candidateComponent = (BeanDefinition)var21.next();
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
//获取feign中的详细信息
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Map attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = this.getClientName(attributes);
//注册配置信息
this.registerClientConfiguration(registry, name, attributes.get("configuration"));
//注册feign客户端
this.registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
注册feign客户端registerFeignClient,包括使用注解时配置的所有信息。当类有@FeignClient注解,将注解的信息取出,连同类名一起取出,赋给BeanDefinitionBuilder,然后根据BeanDefinitionBuilder得到beanDefinition,最后beanDefinition式注入到ioc容器中
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
this.validate(attributes);
definition.addPropertyValue("url", this.getUrl(attributes));
definition.addPropertyValue("path", this.getPath(attributes));
String name = this.getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(2);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = ((Boolean)attributes.get("primary")).booleanValue();
beanDefinition.setPrimary(primary);
String qualifier = this.getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
ReflectiveFeign内部使用了jdk的动态代理为目标接口生成了一个动态代理类,这里会生成一个InvocationHandler(jdk动态代理原理)统一的方法拦截器,同时为接口的每个方法生成一个SynchronousMethodHandler拦截器,并解析方法上的 元数据,生成一个http请求模板。
public T newInstance(Target target) {
//解析方法注解
Map nameToHandler = targetToHandlersByName.apply(target);
Map methodToHandler = new LinkedHashMap();
List defaultMethodHandlers = new LinkedList();
//遍历所有方法,对应上解析后的methodhandler
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,FeignInvocationHandler
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class>[]{target.type()}, handler);
//java8 默认方法处理
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
public Map apply(Target key) {
List metadata = this.contract.parseAndValidatateMetadata(key.type());
Map result = new LinkedHashMap();
MethodMetadata md;
Object buildTemplate;
for(Iterator var4 = metadata.iterator(); var4.hasNext(); result.put(md.configKey(), this.factory.create(key, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder))) {
md = (MethodMetadata)var4.next();
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder);
} else if (md.bodyIndex() != null) {
buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder);
} else {
buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md);
}
}
return result;
}
apply()中使用了this.factory.create(...)方法创建了methodhandler,这个method是动态代理中的对接口方法进行处理的核心类对象SynchronousMethodHandler。
下面的代码可以看到,其包含了feign builder所有的成员变量,创建了SynchronousMethodHandler对象
public MethodHandler create(Target> target, MethodMetadata md, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404);
}
SynchronousMethodHandler对象的核心方法是invoke(),方法名是不是很眼熟,动态代理invacationhandler的invoke最终会调用到这个invoke进行http请求,以下是invacationhandler调用methodhandler的过程
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
return this.executeAndDecode(template);
} catch (RetryableException var5) {
retryer.continueOrPropagate(var5);
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
其中有个executeAndDecode()方法,该方法是通RequestTemplate生成Request请求对象,然后根据用client获取response。
Object executeAndDecode(RequestTemplate template) throws Throwable {
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, this.options);
response.toBuilder().request(request).build();
} catch (IOException var15) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var15, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var15);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
Response var9;
try {
if (this.logLevel != Level.NONE) {
response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel, response, elapsedTime);
response.toBuilder().request(request).build();
}
if (Response.class != this.metadata.returnType()) {
Object var19;
if (response.status() >= 200 && response.status() < 300) {
if (Void.TYPE == this.metadata.returnType()) {
var9 = null;
return var9;
}
var19 = this.decode(response);
return var19;
}
if (this.decode404 && response.status() == 404 && Void.TYPE != this.metadata.returnType()) {
var19 = this.decode(response);
return var19;
}
throw this.errorDecoder.decode(this.metadata.configKey(), response);
}
if (response.body() != null) {
if (response.body().length() != null && (long)response.body().length() <= 8192L) {
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
Response var10 = response.toBuilder().body(bodyData).build();
return var10;
}
shouldClose = false;
var9 = response;
return var9;
}
var9 = response;
} catch (IOException var16) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var16, elapsedTime);
}
throw FeignException.errorReading(request, response, var16);
} finally {
if (shouldClose) {
Util.ensureClosed(response.body());
}
}
return var9;
}
其中Client组件是一个非常重要的组件,Feign最终发送request请求以及接收response响应,都是由Client组件完成的,其中Client的实现类,只要有Client.Default,该类由HttpURLConnnection实现网络请求,另外还支持HttpClient、Okhttp.
首先来看以下在FeignRibbonClient的自动配置类,FeignRibbonClientAutoConfiguration ,主要在工程启动的时候注入一些bean,其代码如下:
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignRibbonClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
}
}
在缺失配置feignClient的情况下,会自动注入new Client.Default(),跟踪Client.Default()源码,它使用的网络请求框架为HttpURLConnection,代码如下:
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection).toBuilder().request(request).build();
}
怎么在feign中使用HttpClient,查看FeignRibbonClientAutoConfiguration的源码
@ConditionalOnClass({ILoadBalancer.class, Feign.class})
@Configuration
@AutoConfigureBefore({FeignAutoConfiguration.class})
@EnableConfigurationProperties({FeignHttpClientProperties.class})
@Import({HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class})
public class FeignRibbonClientAutoConfiguration {
public FeignRibbonClientAutoConfiguration() {
}
@Bean
@Primary
@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
public CachingSpringLoadBalancerFactory cachingLBClientFactory(SpringClientFactory factory) {
return new CachingSpringLoadBalancerFactory(factory);
}
@Bean
@Primary
@ConditionalOnClass(
name = {"org.springframework.retry.support.RetryTemplate"}
)
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
}
@Bean
@ConditionalOnMissingBean
public Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
}
可以发现@Import有导入各个客户端的配置文件,我们点开httpClient的配置文件HttpClientFeignLoadBalancedConfiguration
@Configuration
@ConditionalOnClass({ApacheHttpClient.class})
@ConditionalOnProperty(
value = {"feign.httpclient.enabled"},
matchIfMissing = true
)
class HttpClientFeignLoadBalancedConfiguration {
......省略
}
从代码@ConditionalOnClass(ApacheHttpClient.class)注解可知道,只需要在pom文件加上HttpClient的classpath就行了,另外需要在配置文件上加上feign.httpclient.enabled为true,从 @ConditionalOnProperty注解可知,这个可以不写,在默认的情况下就为true.
在pom文件加上:
com.netflix.feign
feign-httpclient
RELEASE
同理,如果想要feign使用Okhttp,则只需要在pom文件上加上feign-okhttp的依赖:
com.netflix.feign
feign-okhttp
RELEASE
总到来说,Feign的源码实现的过程如下:
feign的调用时序图如下
feign有其他的一些功能如错误处理、重试机制,官方还有hystrix扩展、Rxjava异步扩展、ribon扩展、httpclient的扩展、coder、encoder扩展等。这都归结于feign的良好扩展性。