前言
Feign
是一个面向对象的http
客户端, 这里主要介绍Feign
是如何初始化的, 并如何进行http
请求的.
省略部分不重要的代码. 剥离了hystrix
和ribbon
的相关逻辑.
读完这篇源码解析, 之前写的**SpringCloud-Feign
之重复出现的FeignClientSpecification
**也可以再复习下.
FeignClientsRegistrar 创建 FactoryBean
要启用Feign
, 首先要使用@EnableFeignClients
注解.
1 2 3
| @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { }
|
@EnableFeignClients
注解引入了FeignClientsRegistrar
, 类实现了ImportBeanDefinitionRegistrar
接口, 然后重写了registerBeanDefinitions
方法.
说明加入到了Spring
生命周期的管理中.
1 2 3 4 5 6 7 8 9 10 11
|
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); } }
|
registerBeanDefinitions
方法主要做了两件事
- 将声明了
@EnableFeignClients
的类, 注册到一个新的FeignClientSpecification
实例Bean
中. 作为默认Feign
配置.
- 将
@EnableFeignClients
的声明的包下的所有@FeignClient
修饰的接口, 注册到新的FeignClientFactoryBean
工厂中, 用于创建JDK
动态代理.
registerDefaultConfiguration 注册默认配置
第一行语句注册了一个名为default.配置类名.FeignClientSpecification
的FeignClientSpecification
实例为Bean
.
作为之后创建的Feign
动态代理的默认配置.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name = "default." + metadata.getClassName();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); } } }
|
registerFeignClients 注册 FactoryBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner();
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName()); Class<?>[] clients = attrs.get("clients"); Set<String> basePackages; if (clients == null || clients.length == 0) { scanner.addIncludeFilter(annotationTypeFilter); basePackages = getBasePackages(metadata); } else { }
for (String basePackage : basePackages) { Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { 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());
String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration")); registerFeignClient(registry, annotationMetadata, attributes); } } } } }
|
FeignClientFactoryBean 创建 JDK 动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
@Override public Object getObject() throws Exception { return getTarget(); }
<T> T getTarget() { FeignContext context = this.applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) { return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, this.url)); } Client client = getOptional(context, Client.class); if (client != null) { builder.client(client); } Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url)); } }
public class ReflectiveFeign extends Feign { public <T> T newInstance(Target<T> target) { Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); return proxy; } static class FeignInvocationHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { } } }
|
初始化完毕后, 就可以@Autowired
声明的Feign
接口了.
Feign 调用
1 2 3 4 5 6 7 8 9
| public class ReflectiveFeign extends Feign { static class FeignInvocationHandler implements InvocationHandler { private final Map<Method, MethodHandler> dispatch; public Object invoke(Object proxy, Method method, Object[] args) { return dispatch.get(method).invoke(args); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| final class SynchronousMethodHandler implements MethodHandler { @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 { 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; } } } }
|
调用的时候会根据method
路由到各自的MethodHandler
中.
然后MethodHandler
会创建新的http
客户端, 进行请求操作, 失败的时候做重试.
executeAndDecode 执行http请求并解码响应体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| final class SynchronousMethodHandler implements MethodHandler { Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { Request request = targetRequest(template); Response response; try { response = client.execute(request, options); } catch (IOException e) { throw errorExecuting(request, e); }
try { if (Response.class == metadata.returnType()) { return response; } if (response.status() >= 200 && response.status() < 300) { return decoder.decode(response, metadata.returnType()); } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) { return decoder.decode(response, metadata.returnType()); } else { throw errorDecoder.decode(metadata.configKey(), response); } } catch (IOException e) { throw errorReading(request, response, e); } finally { ensureClosed(response.body()); } } }
|
拦截器的注入时机
拦截器是在创建Feign.Builder
时初始化的, 具体代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware { protected void configureFeign(FeignContext context, Feign.Builder builder) { FeignClientProperties properties = this.applicationContext.getBean(FeignClientProperties.class); if (properties != null) { if (properties.isDefaultToProperties()) { configureUsingConfiguration(context, builder); configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); configureUsingProperties(properties.getConfig().get(this.contextId), builder); } else { configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); configureUsingProperties(properties.getConfig().get(this.contextId), builder); configureUsingConfiguration(context, builder); } } else { configureUsingConfiguration(context, builder); } } }
|
默认情况下, Feign
会依次从Spring
容器、@EnableFeignClients#defaultConfiguration
、@FeignClient#configuration
寻找相关配置.
值得注意的一点是, 不要将@EnableFeignClients#defaultConfiguration
、@FeignClient#configuration
的配置声明为Bean
.
因为Feign
已经将配置注册到FeignClientSpecification
类的实例Bean
中了. 在上面也有提到.
流程图