Talk about FeignClientFactoryBean of spring cloud.

  feign

Order

This article mainly studies the FeignClientFactoryBean of spring cloud.

FeignClientFactoryBean

spring-cloud-openfeign-core-2.2.0.M1-sources.jar! /org/springframework/cloud/openfeign/FeignClientFactoryBean.java

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

    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }

    @Override
    public Class<?> getObjectType() {
        return this.type;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.applicationContext = context;
    }

    <T> T getTarget() {
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
        Feign.Builder builder = feign(context);

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

    //......

}
  • FeignClientFactoryBean implements getObject, getObjectType, isSingleton methods of FactoryBean. Implemented the afterPropertiesSet method of InitializingBean; The setApplicationContext method of ApplicationContextAware is implemented
  • GetObject calls the getTarget method, which takes out the FeignContext from the applicationContext, then constructs Feign.Builder and sets logger, encoder, decoder, contract, After that, according to FeignClientProperties, Feign.Builder’s retryer, errorDecoder, request.Options, requestInterceptors, queryMapEncoder, decode404 are further configured by configuring feignefeign.
  • After preliminary configuration of Feign.Builder, judge whether loadBalance is required. If necessary, set it by loadBalance method. If not, unwrap when Client is LoadBalancerFeignClient.

FeignClientProperties

spring-cloud-openfeign-core-2.2.0.M1-sources.jar! /org/springframework/cloud/openfeign/FeignClientProperties.java

@ConfigurationProperties("feign.client")
public class FeignClientProperties {

    private boolean defaultToProperties = true;

    private String defaultConfig = "default";

    private Map<String, FeignClientConfiguration> config = new HashMap<>();

    public boolean isDefaultToProperties() {
        return this.defaultToProperties;
    }

    public void setDefaultToProperties(boolean defaultToProperties) {
        this.defaultToProperties = defaultToProperties;
    }

    public String getDefaultConfig() {
        return this.defaultConfig;
    }

    public void setDefaultConfig(String defaultConfig) {
        this.defaultConfig = defaultConfig;
    }

    public Map<String, FeignClientConfiguration> getConfig() {
        return this.config;
    }

    public void setConfig(Map<String, FeignClientConfiguration> config) {
        this.config = config;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        FeignClientProperties that = (FeignClientProperties) o;
        return this.defaultToProperties == that.defaultToProperties
                && Objects.equals(this.defaultConfig, that.defaultConfig)
                && Objects.equals(this.config, that.config);
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.defaultToProperties, this.defaultConfig, this.config);
    }

    /**
     * Feign client configuration.
     */
    public static class FeignClientConfiguration {

        private Logger.Level loggerLevel;

        private Integer connectTimeout;

        private Integer readTimeout;

        private Class<Retryer> retryer;

        private Class<ErrorDecoder> errorDecoder;

        private List<Class<RequestInterceptor>> requestInterceptors;

        private Boolean decode404;

        private Class<Decoder> decoder;

        private Class<Encoder> encoder;

        private Class<Contract> contract;

        public Logger.Level getLoggerLevel() {
            return this.loggerLevel;
        }

        public void setLoggerLevel(Logger.Level loggerLevel) {
            this.loggerLevel = loggerLevel;
        }

        public Integer getConnectTimeout() {
            return this.connectTimeout;
        }

        public void setConnectTimeout(Integer connectTimeout) {
            this.connectTimeout = connectTimeout;
        }

        public Integer getReadTimeout() {
            return this.readTimeout;
        }

        public void setReadTimeout(Integer readTimeout) {
            this.readTimeout = readTimeout;
        }

        public Class<Retryer> getRetryer() {
            return this.retryer;
        }

        public void setRetryer(Class<Retryer> retryer) {
            this.retryer = retryer;
        }

        public Class<ErrorDecoder> getErrorDecoder() {
            return this.errorDecoder;
        }

        public void setErrorDecoder(Class<ErrorDecoder> errorDecoder) {
            this.errorDecoder = errorDecoder;
        }

        public List<Class<RequestInterceptor>> getRequestInterceptors() {
            return this.requestInterceptors;
        }

        public void setRequestInterceptors(
                List<Class<RequestInterceptor>> requestInterceptors) {
            this.requestInterceptors = requestInterceptors;
        }

        public Boolean getDecode404() {
            return this.decode404;
        }

        public void setDecode404(Boolean decode404) {
            this.decode404 = decode404;
        }

        public Class<Decoder> getDecoder() {
            return this.decoder;
        }

        public void setDecoder(Class<Decoder> decoder) {
            this.decoder = decoder;
        }

        public Class<Encoder> getEncoder() {
            return this.encoder;
        }

        public void setEncoder(Class<Encoder> encoder) {
            this.encoder = encoder;
        }

        public Class<Contract> getContract() {
            return this.contract;
        }

        public void setContract(Class<Contract> contract) {
            this.contract = contract;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            FeignClientConfiguration that = (FeignClientConfiguration) o;
            return this.loggerLevel == that.loggerLevel
                    && Objects.equals(this.connectTimeout, that.connectTimeout)
                    && Objects.equals(this.readTimeout, that.readTimeout)
                    && Objects.equals(this.retryer, that.retryer)
                    && Objects.equals(this.errorDecoder, that.errorDecoder)
                    && Objects.equals(this.requestInterceptors, that.requestInterceptors)
                    && Objects.equals(this.decode404, that.decode404)
                    && Objects.equals(this.encoder, that.encoder)
                    && Objects.equals(this.decoder, that.decoder)
                    && Objects.equals(this.contract, that.contract);
        }

        @Override
        public int hashCode() {
            return Objects.hash(this.loggerLevel, this.connectTimeout, this.readTimeout,
                    this.retryer, this.errorDecoder, this.requestInterceptors,
                    this.decode404, this.encoder, this.decoder, this.contract);
        }

    }

}
  • FeignClientProperties has a config of Map structure, key is the name of feign client, default is default, and value is FeignClientConfigurationï¼› ; FeignClientConfiguration contains loggerLevel, connectTimeout, readTimeout, retryer, errorDecoder, requestInterceptors, decode404, decoder, encoder, and contract attributes.

Summary

  • FeignClientFactoryBean implements getObject, getObjectType, isSingleton methods of FactoryBean. Implemented the afterPropertiesSet method of InitializingBean; The setApplicationContext method of ApplicationContextAware is implemented
  • GetObject calls the getTarget method, which takes out the FeignContext from the applicationContext, then constructs Feign.Builder and sets logger, encoder, decoder, contract, After that, according to FeignClientProperties, Feign.Builder’s retryer, errorDecoder, request.Options, requestInterceptors, queryMapEncoder, decode404 are further configured by configuring feignefeign.
  • After preliminary configuration of Feign.Builder, judge whether loadBalance is required. If necessary, set it by loadBalance method. If not, unwrap when Client is LoadBalancerFeignClient.

doc