Feign 解析

Fegin 的底层实现机制,是基于动态代理,Fegin 要实现接口调用,要把我们写的接口首先进行动态代理加上发送Http 请求的逻辑,然后把生成后的代理类注入到 Spring 容器当中
通过 @EnableFeignClients 注解上的 @Import 注解, FeignClientsRegistrar 类在这个类里面,实现了 Spring 的支持手动注册 Bean 到容器中的逻辑,通过扫描项目(如果手动配置了 clients 参数的话,就不会扫描项目)把所有的 FeignClient 扫描到,并遍历每个 FeignClient,构建成一个 FeignClientFactoryBean 类,FeignClientFactoryBean 的 getObject 里面会返回当前这个 Bean 的实例,Feign 就是在 getObject 里面添加了动态代理的逻辑

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(FeignClientsRegistrar.class)
  5. public @interface EnableFeignClients {
  6. }

注册 Feign Client 对象

@EnableFeignClients 导入了一个 FeignClientsRegistrar.class 接口,并实现了 ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware 三个接口

  • ImportBeanDefinitionRegistrar:可以手动注册一个 Bean 到 Spring 容器中
  • ResourceLoaderAware:可以读取 classpath 路径下的文件
  • EnvironmentAware:可以读取 Spring 全局配置信息 ```java class FeignClientsRegistrar

    1. implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    @Override public void registerBeanDefinitions(AnnotationMetadata metadata,

    1. BeanDefinitionRegistry registry) {
    2. // 注册默认配置文件
    3. registerDefaultConfiguration(metadata, registry);
    4. // 注册 Fegin Client
    5. registerFeignClients(metadata, registry);

    }

    // 注册默认配置文件 private void registerDefaultConfiguration(AnnotationMetadata metadata,

    1. BeanDefinitionRegistry registry) {
    2. Map<String, Object> defaultAttrs = metadata
    3. .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
    4. if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
    5. String name;
    6. if (metadata.hasEnclosingClass()) {
    7. name = "default." + metadata.getEnclosingClassName();
    8. }
    9. else {
    10. name = "default." + metadata.getClassName();
    11. }
    12. registerClientConfiguration(registry, name,
    13. defaultAttrs.get("defaultConfiguration"));
    14. }

    }

    // 注册 Fegin Client public void registerFeignClients(AnnotationMetadata metadata,

    1. BeanDefinitionRegistry registry) {
    2. // 扫描器
    3. ClassPathScanningCandidateComponentProvider scanner = getScanner();
    4. scanner.setResourceLoader(this.resourceLoader);
    5. Set<String> basePackages;
    6. Map<String, Object> attrs = metadata
    7. .getAnnotationAttributes(EnableFeignClients.class.getName());
    8. // 过滤器
    9. AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
    10. FeignClient.class);
    11. // 判断 @EnableFeignClients 是否定义了 clients
    12. final Class<?>[] clients = attrs == null ? null
    13. : (Class<?>[]) attrs.get("clients");
    14. if (clients == null || clients.length == 0) {
    15. scanner.addIncludeFilter(annotationTypeFilter);
    16. // 扫描包
    17. basePackages = getBasePackages(metadata);
    18. }
    19. else {
    20. final Set<String> clientClasses = new HashSet<>();
    21. basePackages = new HashSet<>();
    22. for (Class<?> clazz : clients) {
    23. basePackages.add(ClassUtils.getPackageName(clazz));
    24. clientClasses.add(clazz.getCanonicalName());
    25. }
    26. AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
    27. @Override
    28. protected boolean match(ClassMetadata metadata) {
    29. String cleaned = metadata.getClassName().replaceAll("\\$", ".");
    30. return clientClasses.contains(cleaned);
    31. }
    32. };
    33. scanner.addIncludeFilter(
    34. new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
    35. }
    36. for (String basePackage : basePackages) {
    37. Set<BeanDefinition> candidateComponents = scanner
    38. .findCandidateComponents(basePackage);
    39. for (BeanDefinition candidateComponent : candidateComponents) {
    40. if (candidateComponent instanceof AnnotatedBeanDefinition) {
    41. // verify annotated class is an interface
    42. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
    43. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
    44. Assert.isTrue(annotationMetadata.isInterface(),
    45. "@FeignClient can only be specified on an interface");
    46. Map<String, Object> attributes = annotationMetadata
    47. .getAnnotationAttributes(
    48. FeignClient.class.getCanonicalName());
    49. // 通过注解属性获取微服务名称
    50. String name = getClientName(attributes);
    51. registerClientConfiguration(registry, name,
    52. attributes.get("configuration"));
    53. // 注册 FeignClient 对象
    54. registerFeignClient(registry, annotationMetadata, attributes);
    55. }
    56. }
    57. }

    }

    private void registerFeignClient(BeanDefinitionRegistry registry,

    1. AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    2. String className = annotationMetadata.getClassName();
    3. //核心代码
    4. BeanDefinitionBuilder definition = BeanDefinitionBuilder
    5. .genericBeanDefinition(FeignClientFactoryBean.class);
  1. validate(attributes);
  2. //赋值操作
  3. definition.addPropertyValue("url", getUrl(attributes));
  4. definition.addPropertyValue("path", getPath(attributes));
  5. String name = getName(attributes);
  6. definition.addPropertyValue("name", name);
  7. String contextId = getContextId(attributes);
  8. definition.addPropertyValue("contextId", contextId);
  9. definition.addPropertyValue("type", className);
  10. definition.addPropertyValue("decode404", attributes.get("decode404"));
  11. definition.addPropertyValue("fallback", attributes.get("fallback"));
  12. definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
  13. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  14. String alias = contextId + "FeignClient";
  15. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  16. boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
  17. // null
  18. beanDefinition.setPrimary(primary);
  19. String qualifier = getQualifier(attributes);
  20. if (StringUtils.hasText(qualifier)) {
  21. alias = qualifier;
  22. }
  23. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
  24. new String[] { alias });
  25. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  26. }

}

  1. <a name="vAi09"></a>
  2. #### FeignClientFactoryBean
  3. ```java
  4. class FeignClientFactoryBean
  5. implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
  6. @Override
  7. public Object getObject() throws Exception {
  8. return getTarget();
  9. }
  10. <T> T getTarget() {
  11. FeignContext context = this.applicationContext.getBean(FeignContext.class);
  12. Feign.Builder builder = feign(context);
  13. if (!StringUtils.hasText(this.url)) {
  14. if (!this.name.startsWith("http")) {
  15. this.url = "http://" + this.name;
  16. }
  17. else {
  18. this.url = this.name;
  19. }
  20. this.url += cleanPath();
  21. // 获取负载均衡器
  22. return (T) loadBalance(builder, context,
  23. new HardCodedTarget<>(this.type, this.name, this.url));
  24. }
  25. if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
  26. this.url = "http://" + this.url;
  27. }
  28. String url = this.url + cleanPath();
  29. Client client = getOptional(context, Client.class);
  30. if (client != null) {
  31. if (client instanceof LoadBalancerFeignClient) {
  32. // not load balancing because we have a url,
  33. // but ribbon is on the classpath, so unwrap
  34. client = ((LoadBalancerFeignClient) client).getDelegate();
  35. }
  36. builder.client(client);
  37. }
  38. Targeter targeter = get(context, Targeter.class);
  39. return (T) targeter.target(this, builder, context,
  40. new HardCodedTarget<>(this.type, this.name, url));
  41. }
  42. protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
  43. HardCodedTarget<T> target) {
  44. Client client = getOptional(context, Client.class);
  45. if (client != null) {
  46. builder.client(client);
  47. Targeter targeter = get(context, Targeter.class);
  48. // 执行代理逻辑
  49. return targeter.target(this, builder, context, target);
  50. }
  51. throw new IllegalStateException(
  52. "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
  53. }
  54. }

返回动态代理类

代理逻辑是由 HystrixTargeter 来实现的

  1. class HystrixTargeter implements Targeter {
  2. @Override
  3. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
  4. FeignContext context, Target.HardCodedTarget<T> target) {
  5. if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
  6. return feign.target(target);
  7. }
  8. feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
  9. String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
  10. : factory.getContextId();
  11. SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
  12. if (setterFactory != null) {
  13. builder.setterFactory(setterFactory);
  14. }
  15. Class<?> fallback = factory.getFallback();
  16. if (fallback != void.class) {
  17. return targetWithFallback(name, context, target, builder, fallback);
  18. }
  19. Class<?> fallbackFactory = factory.getFallbackFactory();
  20. if (fallbackFactory != void.class) {
  21. return targetWithFallbackFactory(name, context, target, builder,
  22. fallbackFactory);
  23. }
  24. return feign.target(target);
  25. }
  26. }
  27. public abstract class Feign {
  28. public <T> T target(Target<T> target) {
  29. return this.build().newInstance(target);
  30. }
  31. public <T> T newInstance(Target<T> target) {
  32. Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
  33. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
  34. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
  35. Method[] var5 = target.type().getMethods();
  36. int var6 = var5.length;
  37. for(int var7 = 0; var7 < var6; ++var7) {
  38. Method method = var5[var7];
  39. if (method.getDeclaringClass() != Object.class) {
  40. if (Util.isDefault(method)) {
  41. DefaultMethodHandler handler = new DefaultMethodHandler(method);
  42. defaultMethodHandlers.add(handler);
  43. methodToHandler.put(method, handler);
  44. } else {
  45. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
  46. }
  47. }
  48. }
  49. InvocationHandler handler = this.factory.create(target, methodToHandler);
  50. T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
  51. Iterator var12 = defaultMethodHandlers.iterator();
  52. while(var12.hasNext()) {
  53. DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
  54. defaultMethodHandler.bindTo(proxy);
  55. }
  56. return proxy;
  57. }
  58. }

Feign 调用逻辑

Feign 会创建一个 InvocationHandler handler = this.factory.create(target, methodToHandler); 这里的实现类是 FeignInvocationHandler 里面由他的 invoke 方法

  1. static class FeignInvocationHandler implements InvocationHandler {
  2. @Override
  3. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  4. if ("equals".equals(method.getName())) {
  5. try {
  6. Object otherHandler =
  7. args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
  8. return equals(otherHandler);
  9. } catch (IllegalArgumentException e) {
  10. return false;
  11. }
  12. } else if ("hashCode".equals(method.getName())) {
  13. return hashCode();
  14. } else if ("toString".equals(method.getName())) {
  15. return toString();
  16. }
  17. return dispatch.get(method).invoke(args);
  18. }
  19. }
  1. final class SynchronousMethodHandler implements MethodHandler {
  2. @Override
  3. public Object invoke(Object[] argv) throws Throwable {
  4. RequestTemplate template = buildTemplateFromArgs.create(argv);
  5. Options options = findOptions(argv);
  6. Retryer retryer = this.retryer.clone();
  7. while (true) {
  8. try {
  9. return executeAndDecode(template, options);
  10. } catch (RetryableException e) {
  11. try {
  12. retryer.continueOrPropagate(e);
  13. } catch (RetryableException th) {
  14. Throwable cause = th.getCause();
  15. if (propagationPolicy == UNWRAP && cause != null) {
  16. throw cause;
  17. } else {
  18. throw th;
  19. }
  20. }
  21. if (logLevel != Logger.Level.NONE) {
  22. logger.logRetry(metadata.configKey(), logLevel);
  23. }
  24. continue;
  25. }
  26. }
  27. }
  28. Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
  29. Request request = targetRequest(template);
  30. if (logLevel != Logger.Level.NONE) {
  31. logger.logRequest(metadata.configKey(), logLevel, request);
  32. }
  33. Response response;
  34. long start = System.nanoTime();
  35. try {
  36. response = client.execute(request, options);
  37. } catch (IOException e) {
  38. if (logLevel != Logger.Level.NONE) {
  39. logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
  40. }
  41. throw errorExecuting(request, e);
  42. }
  43. long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
  44. boolean shouldClose = true;
  45. try {
  46. if (logLevel != Logger.Level.NONE) {
  47. response =
  48. logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
  49. }
  50. if (Response.class == metadata.returnType()) {
  51. if (response.body() == null) {
  52. return response;
  53. }
  54. if (response.body().length() == null ||
  55. response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
  56. shouldClose = false;
  57. return response;
  58. }
  59. // Ensure the response body is disconnected
  60. byte[] bodyData = Util.toByteArray(response.body().asInputStream());
  61. return response.toBuilder().body(bodyData).build();
  62. }
  63. if (response.status() >= 200 && response.status() < 300) {
  64. if (void.class == metadata.returnType()) {
  65. return null;
  66. } else {
  67. Object result = decode(response);
  68. shouldClose = closeAfterDecode;
  69. return result;
  70. }
  71. } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
  72. Object result = decode(response);
  73. shouldClose = closeAfterDecode;
  74. return result;
  75. } else {
  76. throw errorDecoder.decode(metadata.configKey(), response);
  77. }
  78. } catch (IOException e) {
  79. if (logLevel != Logger.Level.NONE) {
  80. logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
  81. }
  82. throw errorReading(request, response, e);
  83. } finally {
  84. if (shouldClose) {
  85. ensureClosed(response.body());
  86. }
  87. }
  88. }
  89. }

执行动态代理

  1. public class LoadBalancerFeignClient implements Client {
  2. @Override
  3. public Response execute(Request request, Request.Options options) throws IOException {
  4. try {
  5. URI asUri = URI.create(request.url());
  6. String clientName = asUri.getHost();
  7. URI uriWithoutHost = cleanUrl(request.url(), clientName);
  8. FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
  9. this.delegate, request, uriWithoutHost);
  10. IClientConfig requestConfig = getClientConfig(options, clientName);
  11. return lbClient(clientName)
  12. .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
  13. }
  14. catch (ClientException e) {
  15. IOException io = findIOException(e);
  16. if (io != null) {
  17. throw io;
  18. }
  19. throw new RuntimeException(e);
  20. }
  21. }
  22. }
  1. public class FeignLoadBalancer extends
  2. AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
  3. @Override
  4. public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
  5. throws IOException {
  6. Request.Options options;
  7. if (configOverride != null) {
  8. RibbonProperties override = RibbonProperties.from(configOverride);
  9. options = new Request.Options(override.connectTimeout(this.connectTimeout),
  10. override.readTimeout(this.readTimeout));
  11. }
  12. else {
  13. options = new Request.Options(this.connectTimeout, this.readTimeout);
  14. }
  15. // 完成方法调用
  16. Response response = request.client().execute(request.toRequest(), options);
  17. return new RibbonResponse(request.getUri(), response);
  18. }
  19. }