Spring Cloud Feign源码分析

 2019-11-23 11:15  阅读(1203)
文章分类:Spring Cloud

如何使用Spring Cloud Feign进行声明式调用

前面文章写过Spring Cloud Feign的介绍,对如何使用Spring Cloud Feign进行了说明,现在再简单回顾一下:
1.在启动类上添加@EnableFeignClients注解,开启FeignClient
2.创建一个FeignClient接口,接口上添加@FeignClient注解,并指明远程调用其他服务的服务名,接口中可以声明方法。
3.在controller通过这个FeignClient进行调用。

下面是介绍的文章。
Spring Cloud Feign介绍

Spring Cloud Feign源码分析:

下面就分析下源码,看看这个调用到底经历了什么。

既然在启动类上添加了@EnableFeignClients注解,那就看看这个注解的源码:

/** * Scans for interfaces that declare they are feign clients (via {@link FeignClient * <code>@FeignClient</code>}). Configures component scanning directives for use with * {@link org.springframework.context.annotation.Configuration * <code>@Configuration</code>} classes. * * @author Spencer Gibb * @author Dave Syer * @since 1.0 */
    @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 {};
    }

从这个注解上面的注释可以知道,这个注解会扫描所有的Feign Client,也就是带有@FeignClient的接口。EnableFeignClients注解上还有个@Import(FeignClientsRegistrar.class),引入了FeignClientsRegistrar这个类,从这个类的名字上看,这是Feign Client注册的一个类,那就再进去这个类看一下代码。
这个类实现了ImportBeanDefinitionRegistrar,ResourceLoaderAware, BeanClassLoaderAware 这三个接口,实现了接口里的方法。
ImportBeanDefinitionRegistrar接口中有个registerBeanDefinitions方法,在FeignClientsRegistrar中进行了实现,下面看下代码:

@Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        registerDefaultConfiguration(metadata, registry);
        registerFeignClients(metadata, registry);
    }

可以看到,在这个方法中,先后调用了两个方法,registerDefaultConfiguration和registerFeignClients。

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;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }

这个方法主要就是扫描EnableFeignClients标签里配置的信息并注册。

public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Set<String> basePackages;

        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        final Class<?>[] clients = attrs == null ? null
                : (Class<?>[]) attrs.get("clients");
        if (clients == null || clients.length == 0) {
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = getBasePackages(metadata);
        }
        else {
            final Set<String> clientClasses = new HashSet<>();
            basePackages = new HashSet<>();
            for (Class<?> clazz : clients) {
                basePackages.add(ClassUtils.getPackageName(clazz));
                clientClasses.add(clazz.getCanonicalName());
            }
            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                @Override
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                    return clientClasses.contains(cleaned);
                }
            };
            scanner.addIncludeFilter(
                    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        }

        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidateComponents = scanner
                    .findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    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);
                }
            }
        }
    }

在registerFeignClients方法中,也是扫描@FeignClient注解修饰的接口,把接口信息和注解上的信息都赋值到BeanDefinitionBuilder中,然后再注入到IOC容器中。

在相同的路径下,还会看到两个配置类:FeignAutoConfiguration和FeignClientsConfiguration。这是自动加载配置类,在FeignClientsConfiguration还可以看到对Hystrix的配置。

在这个路径下还有个FeignClientFactoryBean类,这个类实现了FactoryBean接口。当调用到FeignClient的时候,就会从IOC中读取这个FeignClientFactoryBean并且调用getObject方法。下面就是getObject方法:

@Override
    public Object getObject() throws Exception {
        FeignContext context = applicationContext.getBean(FeignContext.class);
        Feign.Builder builder = feign(context);

        if (!StringUtils.hasText(this.url)) {
            String url;
            if (!this.name.startsWith("http")) {
                url = "http://" + this.name;
            }
            else {
                url = this.name;
            }
            url += cleanPath();
            return 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();
        return targeter.target(this, builder, context, new HardCodedTarget<>(
                this.type, this.name, url));
    }

这个方法先从上下文中获取FeignContext对象,然后通过FeignContext对象构建Feign.Builder。接下来会分两种情况进行处理,一种是@FeignClient没有配置url的时候,会对负载均衡进行集成,一种是@FeignClient配置url的时候,进入最后的target方法看下,方法会调用Feign.Builder的target方法:

public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }

    public Feign build() {
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                               logLevel, decode404);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder,
                                  errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
    }

在target方法中,会调用ReflectiveFeign的newInstance方法。下面再去这个方法里面看下。

/** * creates an api binding to the {@code target}. As this invokes reflection, care should be taken * to cache the result. */
    @SuppressWarnings("unchecked")
    @Override
    public <T> T newInstance(Target<T> target) {
      Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
      Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
      List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

      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;
    }

在newInstance方法中,一开始就调用targetToHandlersByName.apply(target);方法,返回了一个Map<String, MethodHandler>格式的对象,进去这个方法看一下:

public Map<String, MethodHandler> apply(Target key) {
        List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
        Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
        for (MethodMetadata md : metadata) {
          BuildTemplateByResolvingArgs buildTemplate;
          if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
            buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
          } else if (md.bodyIndex() != null) {
            buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
          } else {
            buildTemplate = new BuildTemplateByResolvingArgs(md);
          }
          result.put(md.configKey(),
                     factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
        }
        return result;
      }
    }

这个方法返回的result,key是MethodMetadata的configKey,value是调用factory.create(key, md, buildTemplate, options, decoder, errorDecoder)返回的对象,那就再进去方法看下:

public MethodHandler create(Target<?> target, MethodMetadata md,
                                RequestTemplate.Factory buildTemplateFromArgs,
                                Options options, Decoder decoder, ErrorDecoder errorDecoder) {
      return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
                                          logLevel, md, buildTemplateFromArgs, options, decoder,
                                          errorDecoder, decode404);
    }

这个方法直接就是新建了一个SynchronousMethodHandler对象,SynchronousMethodHandler对象中,有个invoke方法。

@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) {
          retryer.continueOrPropagate(e);
          if (logLevel != Logger.Level.NONE) {
            logger.logRetry(metadata.configKey(), logLevel);
          }
          continue;
        }
      }
    }

SynchronousMethodHandler类就行进行拦截处理的,实际执行这个invoke方法,通过参数生成RequestTemplate对象,然后调用executeAndDecode方法,通过RequestTemplate构建request,通过http client调用,获取Response。


来源:http://ddrv.cn/a/88268

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring Cloud Feign源码分析

相关推荐