spring clound openfeign 原理解析(一) — Ribbon

在前面章节中我们介绍了ribbon的使用以及启动原理,在这篇文章中将主要介绍openfeign的原理。在Spring cloud体系中, feign其实有着很重比较重要的地位,因为feign能够大大简化我们对远程请求以及返回结果的处理,帮助我们快速开发。我们还是以一个简单的demo开始我们的代码调试和讲解步骤.

DEMO

对于feign中, spring还是集成的很好的,同时也为我们提供了扩展的地方。

引入依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

启用Feign

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SpringEurekaClientDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringEurekaClientDemoApplication.class, args);
    }
}

定义FeignClient

package org.spring.learn.eureka.client.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = "SPRING-EUREKA-CLIENT-B")
public interface InfoFeignClient {

    @GetMapping("/info")
    String info();
}

使用FeignClient类型

package org.spring.learn.eureka.client.controller;

import org.spring.learn.eureka.client.feign.InfoFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class InfoController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private InfoFeignClient infoFeignClient;

    @GetMapping("/info")
    public String info() {
        return restTemplate.getForObject("http://SPRING-EUREKA-CLIENT-B/info", String.class);
    }

    @GetMapping("/feign/info")
    public String feignInfo() {
        return infoFeignClient.info();
    }
}

通过以上步骤,我们可以知道,在通过FeignClient注解定义的接口, 可以通过@Autowired直接使用。我当前给的示例中,其实是与ribbon相结合使用的, 因此同时也具备类ribbon负载均衡的能力。其实对于我们开发而言,大大简化了我们的开发难度。

EnableFeignClients

该注解为启用Feign的一个标识注解,并且在启用的过程中,也会做其他特殊的操作:

  • 如果包含defaultConfiguration配置,则默认为加载@Configuration的配置类
  • 如果包含了basePackages, basePackageClasses, value的值设置,则扫表配置下所有类型的FeignClient
  • 如果以上配置都为空,则默认从启动类Application所在目录查找所有FeignClient

该类的具体源码如下:

package org.springframework.cloud.openfeign;

/**
 * Scans for interfaces that declare they are feign clients (via
 * {@link org.springframework.cloud.openfeign.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 {

    /**
     * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
     * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
     * {@code @ComponentScan(basePackages="org.my.pkg")}.
     * @return the array of 'basePackages'.
     */
    String[] value() default {};

    /**
     * Base packages to scan for annotated components.
     * <p>
     * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
     * <p>
     * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
     * package names.
     * @return the array of 'basePackages'.
     */
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages()} for specifying the packages to
     * scan for annotated components. The package of each class specified will be scanned.
     * <p>
     * Consider creating a special no-op marker class or interface in each package that
     * serves no purpose other than being referenced by this attribute.
     * @return the array of 'basePackageClasses'.
     */
    Class<?>[] basePackageClasses() default {};

    /**
     * A custom <code>@Configuration</code> for all feign clients. Can contain override
     * <code>@Bean</code> definition for the pieces that make up the client, for instance
     * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
     *
     * @see FeignClientsConfiguration for the defaults
     * @return list of default configurations
     */
    Class<?>[] defaultConfiguration() default {};

    /**
     * List of classes annotated with @FeignClient. If not empty, disables classpath
     * scanning.
     * @return list of FeignClient classes
     */
    Class<?>[] clients() default {};

}

该注解在使用的过程中,会通过@Import注入BeanDefinition对象到容器中, 这部分内容可以参考Import注解使用详解

FeignClientsRegistrar

package org.springframework.cloud.openfeign;


/**
 * @author Spencer Gibb
 * @author Jakub Narloch
 * @author Venil Noronha
 * @author Gang Li
 */
class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    // patterned after Spring Integration IntegrationComponentScanRegistrar
    // and RibbonClientsConfigurationRegistgrar

    // 资源加载器
    private ResourceLoader resourceLoader;
    
    // 环境变量上下文
    private Environment environment;

    FeignClientsRegistrar() {
    }

    static void validateFallback(final Class clazz) {
        Assert.isTrue(!clazz.isInterface(),
                "Fallback class must implement the interface annotated by @FeignClient");
    }

    static void validateFallbackFactory(final Class clazz) {
        Assert.isTrue(!clazz.isInterface(), "Fallback factory must produce instances "
                + "of fallback classes that implement the interface annotated by @FeignClient");
    }

    static String getName(String name) {
        if (!StringUtils.hasText(name)) {
            return "";
        }

        String host = null;
        try {
            String url;
            if (!name.startsWith("http://") && !name.startsWith("https://")) {
                url = "http://" + name;
            }
            else {
                url = name;
            }
            host = new URI(url).getHost();

        }
        catch (URISyntaxException e) {
        }
        Assert.state(host != null, "Service id not legal hostname (" + name + ")");
        return name;
    }

    static String getUrl(String url) {
        if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
            if (!url.contains("://")) {
                url = "http://" + url;
            }
            try {
                new URL(url);
            }
            catch (MalformedURLException e) {
                throw new IllegalArgumentException(url + " is malformed", e);
            }
        }
        return url;
    }

    static String getPath(String path) {
        if (StringUtils.hasText(path)) {
            path = path.trim();
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (path.endsWith("/")) {
                path = path.substring(0, path.length() - 1);
            }
        }
        return path;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
         // 注册Configuration
        registerDefaultConfiguration(metadata, registry);
        // 扫描并注册所有FeignClient
        registerFeignClients(metadata, registry);
    }

    private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 获取注解EnableFeignClients下所有属性
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        // 判断是否包含了defaultConfiguration配置
        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            // 注册defaultConfiguration中的配置类到Spring Context中
            registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
        }
    }

    // 扫描并注册所有FeignClient类型
    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);

        // 获取注解中clients定义列表
        final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");

        // 当clients不为空时,则读取basepackges, basePackageClasses, value属性,并返回扫描路径列表
        if (clients == null || clients.length == 0) {
            // 新增按照类型扫描过滤器
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = getBasePackages(metadata);
        }
        else {
            // 当包含了Clients指定类时
            final Set<String> clientClasses = new HashSet<>();
            basePackages = new HashSet<>();
            // 变量并保存Client类型的名称
            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);
                }
            };

            // 新增注解过滤器,以及Client类型过滤器
            scanner.addIncludeFilter(
                    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        }

        for (String basePackage : basePackages) {
            // 扫描并获取package下所有包含@FeignClient注解的类型,并解析为BeanDefinition
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            // 遍历BeanDefinition
            for (BeanDefinition candidateComponent : candidateComponents) {
                // 判断BeanDefinition类型
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;

                    // 获取BeanDefinition元数据信息
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    // 判断当前类型是否为接口
                    Assert.isTrue(annotationMetadata.isInterface(),
                            "@FeignClient can only be specified on an interface");

                    // 获取FeignClient注解所有元数据信息
                    Map<String, Object> attributes = annotationMetadata
                            .getAnnotationAttributes(
                                    FeignClient.class.getCanonicalName());
                    // 获取FeignClient客户端名称
                    String name = getClientName(attributes);
                    // 注册客户端信息配置信息
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));

                    // 注册客户端
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }

    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        // 获取注解所在的class全限定名称
        String className = annotationMetadata.getClassName();
        // 创建FeignClientFactoryBean对应BeanDefinition
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
      
        // 验证参数配置
        validate(attributes);
        // 获取并解析url信息
        definition.addPropertyValue("url", getUrl(attributes));
        // 获取并解析path配置
        definition.addPropertyValue("path", getPath(attributes));
        // 获取client名称
        String name = getName(attributes);
        // 设置name属性
        definition.addPropertyValue("name", name);
        // 获取contextId
        String contextId = getContextId(attributes);
        // 设置contextId属性
        definition.addPropertyValue("contextId", contextId);
        // 设置type属性
        definition.addPropertyValue("type", className);
        // 设置decode404属性
        definition.addPropertyValue("decode404", attributes.get("decode404"));
        // 设置fallback属性
        definition.addPropertyValue("fallback", attributes.get("fallback"));
        // 设置FallbackFactory属性
        definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
        // 设置自动注入类型
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        // 设置beanDefinition别名信息
        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

        // 获取primary属性
        boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
                                                                // null
        // 设置BeanDefinition primary属性
        beanDefinition.setPrimary(primary);

        String qualifier = getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }
         
        // 创建BeanDefinitionHolder
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        // 注册BeanDefinition
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

...

    // 注册FeignClientSpecification配置到spring容器中
    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {

        // 创建BeanDefinition对象
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
        // 设置构造器入参信息
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        // 注入到Spring 上下文
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    /**
     * Helper class to create a {@link TypeFilter} that matches if all the delegates
     * match.
     *
     * @author Oliver Gierke
     */
    private static class AllTypeFilter implements TypeFilter {

        private final List<TypeFilter> delegates;

        /**
         * Creates a new {@link AllTypeFilter} to match if all the given delegates match.
         * @param delegates must not be {@literal null}.
         */
        AllTypeFilter(List<TypeFilter> delegates) {
            Assert.notNull(delegates, "This argument is required, it must not be null");
            this.delegates = delegates;
        }

        @Override
        public boolean match(MetadataReader metadataReader,
                MetadataReaderFactory metadataReaderFactory) throws IOException {

            for (TypeFilter filter : this.delegates) {
                if (!filter.match(metadataReader, metadataReaderFactory)) {
                    return false;
                }
            }

            return true;
        }

    }

}

以上就是Feign将FeignClient注解的类型扫描转换成BeanDefinitionHolder然后注册到spring的过程。在这过程中有几个点需要注意:

  • 通过FeignClient注解的类型一定需要为接口
  • 在注入到Spring中的BeanDefinition使用的是BeanDefinitionHolder
  • Feign是注入到Spring是关于FeingClientFactoryBean对象, 并不是直接通过代理的方式创建的

FeignContext

@Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {

@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}

}

在Feign自动装配的过程中,最重要的一个类型则是FeignContext对象,该对象在后面的实现中充当着很重要的作用

FeignClientFactoryBean

接下来是作为Feign至关重要的实现,在上面的解析过程中我们介绍了,Feign并没有直接创建代理实现功能,而是通过Spring FactoryBean的功能,实现对代理类型的延迟初始化。这里我们可以看下该类的主要源码:

package org.springframework.cloud.openfeign;

import java.util.Map;
import java.util.Objects;

import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.Request;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.Target.HardCodedTarget;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * @author Spencer Gibb
 * @author Venil Noronha
 * @author Eko Kurniawan Khannedy
 * @author Gregor Zurowski
 */
class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

    /***********************************
     * WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some
     * lifecycle race condition.
     ***********************************/

    private Class<?> type;

    private String name;

    private String url;

    private String contextId;

    private String path;

    private boolean decode404;

    private ApplicationContext applicationContext;

    private Class<?> fallback = void.class;

    private Class<?> fallbackFactory = void.class;

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.hasText(this.contextId, "Context id must be set");
        Assert.hasText(this.name, "Name must be set");
    }

    protected Feign.Builder feign(FeignContext context) {
        // 获取FeignLoggerFactory对象
        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
        Logger logger = loggerFactory.create(this.type);

        // 创建并设置Encoder, Decoder, Contract类型bean信息
        // @formatter:off
        Feign.Builder builder = get(context, Feign.Builder.class)
                // required values
                .logger(logger)
                .encoder(get(context, Encoder.class))
                .decoder(get(context, Decoder.class))
                .contract(get(context, Contract.class));
        // @formatter:on
        // 配置feign
        configureFeign(context, builder);

        return builder;
    }

    protected void configureFeign(FeignContext context, Feign.Builder builder) {
        // 获取关于Feign的配置信息列表
        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);
        }
    }

    protected void configureUsingConfiguration(FeignContext context,
            Feign.Builder builder) {
        // 获取日志等级
        Logger.Level level = getOptional(context, Logger.Level.class);
        if (level != null) {
            builder.logLevel(level);
        }

        // 获取重试器
        Retryer retryer = getOptional(context, Retryer.class);
        if (retryer != null) {
            builder.retryer(retryer);
        }

        // 获取错误解码器
        ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
        if (errorDecoder != null) {
            builder.errorDecoder(errorDecoder);
        }

        // 请求参数信息
        Request.Options options = getOptional(context, Request.Options.class);
        if (options != null) {
            builder.options(options);
        }

        // 请求拦截器
        Map<String, RequestInterceptor> requestInterceptors = context
                .getInstances(this.contextId, RequestInterceptor.class);
        if (requestInterceptors != null) {
            builder.requestInterceptors(requestInterceptors.values());
        }

        // 是否解码404
        if (this.decode404) {
            builder.decode404();
        }
    }

    protected void configureUsingProperties(
            FeignClientProperties.FeignClientConfiguration config,
            Feign.Builder builder) {
        if (config == null) {
            return;
        }

        if (config.getLoggerLevel() != null) {
            builder.logLevel(config.getLoggerLevel());
        }

        if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
            builder.options(new Request.Options(config.getConnectTimeout(),
                    config.getReadTimeout()));
        }

        if (config.getRetryer() != null) {
            Retryer retryer = getOrInstantiate(config.getRetryer());
            builder.retryer(retryer);
        }

        if (config.getErrorDecoder() != null) {
            ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
            builder.errorDecoder(errorDecoder);
        }

        if (config.getRequestInterceptors() != null
                && !config.getRequestInterceptors().isEmpty()) {
            // this will add request interceptor to builder, not replace existing
            for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
                RequestInterceptor interceptor = getOrInstantiate(bean);
                builder.requestInterceptor(interceptor);
            }
        }

        if (config.getDecode404() != null) {
            if (config.getDecode404()) {
                builder.decode404();
            }
        }

        if (Objects.nonNull(config.getEncoder())) {
            builder.encoder(getOrInstantiate(config.getEncoder()));
        }

        if (Objects.nonNull(config.getDecoder())) {
            builder.decoder(getOrInstantiate(config.getDecoder()));
        }

        if (Objects.nonNull(config.getContract())) {
            builder.contract(getOrInstantiate(config.getContract()));
        }
    }

    private <T> T getOrInstantiate(Class<T> tClass) {
        try {
            return this.applicationContext.getBean(tClass);
        }
        catch (NoSuchBeanDefinitionException e) {
            return BeanUtils.instantiateClass(tClass);
        }
    }

    protected <T> T get(FeignContext context, Class<T> type) {
        T instance = context.getInstance(this.contextId, type);
        if (instance == null) {
            throw new IllegalStateException(
                    "No bean found of type " + type + " for " + this.contextId);
        }
        return instance;
    }

    protected <T> T getOptional(FeignContext context, Class<T> type) {
        return context.getInstance(this.contextId, type);
    }

    protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
            HardCodedTarget<T> 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 {
        // 当在Spring声明周期中,如果判断类型为FactoryBean的时候,则会调用该方法创建对象
        return getTarget();
    }

    /**
     * @param <T> the target type of the Feign client
     * @return a {@link Feign} client created with the specified data and the context
     * information
     */
    <T> T getTarget() {
        // 获取FeignContext对象
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
        // 生成Feign.Builder对象
        Feign.Builder builder = feign(context);

        // 判断当前url是否存在, 当url不存在时,则认为需要通过负载均衡的方式请求,也就是需要与ribbon整合
        if (!StringUtils.hasText(this.url)) {
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            }
            else {
                this.url = this.name;
            }
            this.url += cleanPath();
            // 返回LoadBalancer对象
            return (T) loadBalance(builder, context,
                    new HardCodedTarget<>(this.type, this.name, this.url));
        }

        // 当url存在的时候, 则拼接http信息
        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) {
            // 如果Client为ribbon支持时,也不使用ribbon
            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));
    }

    private String cleanPath() {
        String path = this.path.trim();
        if (StringUtils.hasLength(path)) {
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (path.endsWith("/")) {
                path = path.substring(0, path.length() - 1);
            }
        }
        return path;
    }
}

以上就是Feign的初始化过程,通过源码我们可以知道, Feign使用的JDK自身的代码方式进行代码,而spring feign只是通过集成的方式让我们能够更好的使用Feign的特性。

Show 2 Comments

2 Comments

No comments yet. Why don’t you start the discussion?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注