OpenFeign遠程調用原理


  之前對OpenFeign 了解到只用在接口上面打個注解,然后就可以通過內部調用去調用遠程地址。研究完Feign生成對象以及代理對象的作用過程之后發現這個過程用到了Spring的好多東西,在之后的過程中可以借鑒這種思想。

  查看Springboot項目一般從Enable入口,然后查看倒入的類。然后分析其IoC過程: 包括注冊BeanDefinition、生成單例對象。

0. 以一個Feign 接口查看

接口如下:

package cn.qz.cloud.service;

import cn.qz.cloud.utils.JSONResultUtil;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {

    @GetMapping(value = "/pay/listAll")
    JSONResultUtil<List<Map<String, Object>>> listAll();

    @GetMapping("/pay/getServerPort")
    JSONResultUtil<String> getServerPort();

    /**
     * 注意
     * 1. PathVariable 的value 屬性必須有。 會在org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor#processArgument(org.springframework.cloud.openfeign.AnnotatedParameterProcessor.AnnotatedParameterContext, java.lang.annotation.Annotation, java.lang.reflect.Method) 進行驗證
     *
     * @param map
     * @param queryParam
     * @param id
     * @return
     */
    @PostMapping("/pay/testParam/{id}")
//    JSONResultUtil<Map<String, Object>> testParam(Map<String, Object> map, String queryParam, String id);
    JSONResultUtil<Map<String, Object>> testParam(@RequestBody Map<String, Object> map, @RequestParam("queryParam") String queryParam, @PathVariable("id") String id);

    /**
     * 當只有一個參數時可不寫@RequestBody
     *
     * @param map
     * @return
     */
    @PostMapping("/pay/testParam2")
    JSONResultUtil<Map<String, Object>> testParam2(Map<String, Object> map);

}

1.  org.springframework.cloud.openfeign.EnableFeignClients源碼查看

源碼如下:

package org.springframework.cloud.openfeign;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

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

可以看到是Import導入了FeignClientsRegistrar

package org.springframework.cloud.openfeign;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    private ResourceLoader resourceLoader;
    private Environment environment;

    FeignClientsRegistrar() {
    }

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

    static void validateFallbackFactory(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 "";
        } else {
            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 var3) {
                ;
            }

            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 var2) {
                throw new IllegalArgumentException(url + " is malformed", var2);
            }
        }

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

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

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        this.registerDefaultConfiguration(metadata, registry);
        this.registerFeignClients(metadata, registry);
    }

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

            this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
        }

    }

    public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
        Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
        Object basePackages;
        if (clients != null && clients.length != 0) {
            final Set<String> clientClasses = new HashSet();
            basePackages = new HashSet();
            Class[] var9 = clients;
            int var10 = clients.length;

            for(int var11 = 0; var11 < var10; ++var11) {
                Class<?> clazz = var9[var11];
                ((Set)basePackages).add(ClassUtils.getPackageName(clazz));
                clientClasses.add(clazz.getCanonicalName());
            }

            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                    return clientClasses.contains(cleaned);
                }
            };
            scanner.addIncludeFilter(new FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        } else {
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = this.getBasePackages(metadata);
        }

        Iterator var17 = ((Set)basePackages).iterator();

        while(var17.hasNext()) {
            String basePackage = (String)var17.next();
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            Iterator var21 = candidateComponents.iterator();

            while(var21.hasNext()) {
                BeanDefinition candidateComponent = (BeanDefinition)var21.next();
                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 = this.getClientName(attributes);
                    this.registerClientConfiguration(registry, name, attributes.get("configuration"));
                    this.registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }

    }

    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
        this.validate(attributes);
        definition.addPropertyValue("url", this.getUrl(attributes));
        definition.addPropertyValue("path", this.getPath(attributes));
        String name = this.getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = this.getContextId(attributes);
        definition.addPropertyValue("contextId", contextId);
        definition.addPropertyValue("type", className);
        definition.addPropertyValue("decode404", attributes.get("decode404"));
        definition.addPropertyValue("fallback", attributes.get("fallback"));
        definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
        definition.setAutowireMode(2);
        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        boolean primary = ((Boolean)attributes.get("primary")).booleanValue();
        beanDefinition.setPrimary(primary);
        String qualifier = this.getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    private void validate(Map<String, Object> attributes) {
        AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
        validateFallback(annotation.getClass("fallback"));
        validateFallbackFactory(annotation.getClass("fallbackFactory"));
    }

    String getName(Map<String, Object> attributes) {
        String name = (String)attributes.get("serviceId");
        if (!StringUtils.hasText(name)) {
            name = (String)attributes.get("name");
        }

        if (!StringUtils.hasText(name)) {
            name = (String)attributes.get("value");
        }

        name = this.resolve(name);
        return getName(name);
    }

    private String getContextId(Map<String, Object> attributes) {
        String contextId = (String)attributes.get("contextId");
        if (!StringUtils.hasText(contextId)) {
            return this.getName(attributes);
        } else {
            contextId = this.resolve(contextId);
            return getName(contextId);
        }
    }

    private String resolve(String value) {
        return StringUtils.hasText(value) ? this.environment.resolvePlaceholders(value) : value;
    }

    private String getUrl(Map<String, Object> attributes) {
        String url = this.resolve((String)attributes.get("url"));
        return getUrl(url);
    }

    private String getPath(Map<String, Object> attributes) {
        String path = this.resolve((String)attributes.get("path"));
        return getPath(path);
    }

    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {
                    isCandidate = true;
                }

                return isCandidate;
            }
        };
    }

    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
        Set<String> basePackages = new HashSet();
        String[] var4 = (String[])((String[])attributes.get("value"));
        int var5 = var4.length;

        int var6;
        String pkg;
        for(var6 = 0; var6 < var5; ++var6) {
            pkg = var4[var6];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        var4 = (String[])((String[])attributes.get("basePackages"));
        var5 = var4.length;

        for(var6 = 0; var6 < var5; ++var6) {
            pkg = var4[var6];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        Class[] var8 = (Class[])((Class[])attributes.get("basePackageClasses"));
        var5 = var8.length;

        for(var6 = 0; var6 < var5; ++var6) {
            Class<?> clazz = var8[var6];
            basePackages.add(ClassUtils.getPackageName(clazz));
        }

        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }

        return basePackages;
    }

    private String getQualifier(Map<String, Object> client) {
        if (client == null) {
            return null;
        } else {
            String qualifier = (String)client.get("qualifier");
            return StringUtils.hasText(qualifier) ? qualifier : null;
        }
    }

    private String getClientName(Map<String, Object> client) {
        if (client == null) {
            return null;
        } else {
            String value = (String)client.get("contextId");
            if (!StringUtils.hasText(value)) {
                value = (String)client.get("value");
            }

            if (!StringUtils.hasText(value)) {
                value = (String)client.get("name");
            }

            if (!StringUtils.hasText(value)) {
                value = (String)client.get("serviceId");
            }

            if (StringUtils.hasText(value)) {
                return value;
            } else {
                throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
            }
        }
    }

    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());
    }

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

    private static class AllTypeFilter implements TypeFilter {
        private final List<TypeFilter> delegates;

        AllTypeFilter(List<TypeFilter> delegates) {
            Assert.notNull(delegates, "This argument is required, it must not be null");
            this.delegates = delegates;
        }

        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            Iterator var3 = this.delegates.iterator();

            TypeFilter filter;
            do {
                if (!var3.hasNext()) {
                    return true;
                }

                filter = (TypeFilter)var3.next();
            } while(filter.match(metadataReader, metadataReaderFactory));

            return false;
        }
    }
}

  在之前了解到IoC過程中會掃描到Import導入的類,然后判斷是ImportBeanDefinitionRegistrar 的話會進行反射創建對象,然后調用Aware 相關方法,然后調用 registerBeanDefinitions 方法動態的注冊Bean。

  所以核心入口就是registerBeanDefinitions  方法。

2. 注冊到Spring 容器中過程

1. registerDefaultConfiguration(metadata, registry); 方法詳解

  注冊了一個org.springframework.cloud.openfeign.FeignClientSpecification 類

2. registerFeignClients(metadata, registry); 方法詳解==注冊Bean的核心也是在這里

  這個就是掃描指定包下面所有帶FeignClient 注解的類,然后掃描到之后解析屬性,最后注冊到IoC容器中。IoC容器中維護的是FeignClientFactoryBean 一個工廠Bean, 也就是真正服務於業務的bean會在其getObject()方法生成。所以核心就是查看這個FactoryBean 的生成過程。

1. 調用方法org.springframework.cloud.openfeign.FeignClientsRegistrar#getScanner 獲取一個scanner, 用於掃描classpath 路徑下指定的類。(這也是Spring 的套路)

2. scanner.setResourceLoader(this.resourceLoader);    給scanner 設置一個resourceLoader。 真正干活的是這個resourceLoader 去加載

3. 下面代碼給scanner 家里一個掃描包含的注解類型,就是我們的FeignClient 注解。 

AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class); 

scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));

4. 然后獲取到EnableFeignClients 注解上的屬性,如下:

 沒有指定掃描的包名,然后會取EnableFeignClients  所在類的包作為包路徑進行掃描

5. 接下來就是遍歷包名稱集合,然后掃描得到BeanDefinition 對象。 然后獲取到注解上面的屬性,然后創建一個FeignClientFactoryBean 對象,並且將注解上的相關屬性賦給Beandefinition內部的propertyValues 屬性中。

(1) Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage); 代碼利用新建的掃描器掃描指定包中包含@FeignClient 注解的對象,

最終掃描會調用到org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(這個也是SpringIoC過程自動掃描的重要方法)

掃描出的beanDefinition 對象包含一些重要的信息,包括:

 

(2) 接下來獲取掃描到的注解上的屬性。

(3) 然后調用org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient進行注冊到IoC容器中

第一步創建一個FeignClientFactoryBean構造器BeanDefinitionBuilder(從這里看出來我們后面拿到的service 對象實際是這個工廠生產出來的對象)

第二步解析注解上面的屬性,對name、url、path、fallback等屬性進行特殊處理后設置到BeanDefinition 的一個propertyValues 屬性中記錄起來(最后容器在創建對象完成屬性注入時會根據BeanDefinition的propertyValues中的屬性進行注入,這個是SpringIoC創建對象過程中的一個操作)。

第三步設置其primary 屬性為true,然后生成一個alias 別名

第四步就是調用BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); 注冊到IoC容器中。

3. 接下來研究對象的創建

  在上面了解到@FeignClient 只能用於接口中。

  注入到容器的是一個FactoryBean, 則真正生成Bean 是FactoryBean的getObject 方法,而且是FactoryBean的創建是在IoC容器過程中創建的。SpringIoC創建完對象會先反射創建對象,然后屬性注入,屬性注入過程中會根據BeanDefinition對象的propertyValues 給反射的對象進行屬性注入。

  FactoryBean.getObject方法生成對象會在第一次使用bean時創建, 而不是在容器啟動過程中就創建(也就是如果只聲明不使用FactoryBean生成的對象不會進行創建)。

  我們需要的target對象是一個接口,所以是需要用到JDK的動態代理來生成代理對象然后服務於業務。

org.springframework.cloud.openfeign.FeignClientFactoryBean源碼如下:

package org.springframework.cloud.openfeign;

import feign.Client;
import feign.Contract;
import feign.Logger;
import feign.QueryMapEncoder;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.Feign.Builder;
import feign.Logger.Level;
import feign.Request.Options;
import feign.Target.HardCodedTarget;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
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.FeignClientProperties.FeignClientConfiguration;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
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;

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
    private Class<?> type;
    private String name;
    private String url;
    private String contextId;
    private String path;
    private boolean decode404;
    private ApplicationContext applicationContext;
    private Class<?> fallback;
    private Class<?> fallbackFactory;

    FeignClientFactoryBean() {
        this.fallback = Void.TYPE;
        this.fallbackFactory = Void.TYPE;
    }

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

    protected Builder feign(FeignContext context) {
        FeignLoggerFactory loggerFactory = (FeignLoggerFactory)this.get(context, FeignLoggerFactory.class);
        Logger logger = loggerFactory.create(this.type);
        Builder builder = ((Builder)this.get(context, Builder.class)).logger(logger).encoder((Encoder)this.get(context, Encoder.class)).decoder((Decoder)this.get(context, Decoder.class)).contract((Contract)this.get(context, Contract.class));
        this.configureFeign(context, builder);
        return builder;
    }

    protected void configureFeign(FeignContext context, Builder builder) {
        FeignClientProperties properties = (FeignClientProperties)this.applicationContext.getBean(FeignClientProperties.class);
        if (properties != null) {
            if (properties.isDefaultToProperties()) {
                this.configureUsingConfiguration(context, builder);
                this.configureUsingProperties((FeignClientConfiguration)properties.getConfig().get(properties.getDefaultConfig()), builder);
                this.configureUsingProperties((FeignClientConfiguration)properties.getConfig().get(this.contextId), builder);
            } else {
                this.configureUsingProperties((FeignClientConfiguration)properties.getConfig().get(properties.getDefaultConfig()), builder);
                this.configureUsingProperties((FeignClientConfiguration)properties.getConfig().get(this.contextId), builder);
                this.configureUsingConfiguration(context, builder);
            }
        } else {
            this.configureUsingConfiguration(context, builder);
        }

    }

    protected void configureUsingConfiguration(FeignContext context, Builder builder) {
        Level level = (Level)this.getOptional(context, Level.class);
        if (level != null) {
            builder.logLevel(level);
        }

        Retryer retryer = (Retryer)this.getOptional(context, Retryer.class);
        if (retryer != null) {
            builder.retryer(retryer);
        }

        ErrorDecoder errorDecoder = (ErrorDecoder)this.getOptional(context, ErrorDecoder.class);
        if (errorDecoder != null) {
            builder.errorDecoder(errorDecoder);
        }

        Options options = (Options)this.getOptional(context, 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());
        }

        QueryMapEncoder queryMapEncoder = (QueryMapEncoder)this.getOptional(context, QueryMapEncoder.class);
        if (queryMapEncoder != null) {
            builder.queryMapEncoder(queryMapEncoder);
        }

        if (this.decode404) {
            builder.decode404();
        }

    }

    protected void configureUsingProperties(FeignClientConfiguration config, Builder builder) {
        if (config != null) {
            if (config.getLoggerLevel() != null) {
                builder.logLevel(config.getLoggerLevel());
            }

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

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

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

            if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
                Iterator var7 = config.getRequestInterceptors().iterator();

                while(var7.hasNext()) {
                    Class<RequestInterceptor> bean = (Class)var7.next();
                    RequestInterceptor interceptor = (RequestInterceptor)this.getOrInstantiate(bean);
                    builder.requestInterceptor(interceptor);
                }
            }

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

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

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

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

        }
    }

    private <T> T getOrInstantiate(Class<T> tClass) {
        try {
            return this.applicationContext.getBean(tClass);
        } catch (NoSuchBeanDefinitionException var3) {
            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);
        } else {
            return instance;
        }
    }

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

    protected <T> T loadBalance(Builder builder, FeignContext context, HardCodedTarget<T> target) {
        Client client = (Client)this.getOptional(context, Client.class);
        if (client != null) {
            builder.client(client);
            Targeter targeter = (Targeter)this.get(context, Targeter.class);
            return targeter.target(this, builder, context, target);
        } else {
            throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
        }
    }

    public Object getObject() throws Exception {
        return this.getTarget();
    }

    <T> T getTarget() {
        FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
        Builder builder = this.feign(context);
        if (!StringUtils.hasText(this.url)) {
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            } else {
                this.url = this.name;
            }

            this.url = this.url + this.cleanPath();
            return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
        } else {
            if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
                this.url = "http://" + this.url;
            }

            String url = this.url + this.cleanPath();
            Client client = (Client)this.getOptional(context, Client.class);
            if (client != null) {
                if (client instanceof LoadBalancerFeignClient) {
                    client = ((LoadBalancerFeignClient)client).getDelegate();
                }

                if (client instanceof FeignBlockingLoadBalancerClient) {
                    client = ((FeignBlockingLoadBalancerClient)client).getDelegate();
                }

                builder.client(client);
            }

            Targeter targeter = (Targeter)this.get(context, Targeter.class);
            return 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;
    }

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

    public boolean isSingleton() {
        return true;
    }

    public Class<?> getType() {
        return this.type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContextId() {
        return this.contextId;
    }

    public void setContextId(String contextId) {
        this.contextId = contextId;
    }

    public String getUrl() {
        return this.url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getPath() {
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public boolean isDecode404() {
        return this.decode404;
    }

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

    public ApplicationContext getApplicationContext() {
        return this.applicationContext;
    }

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

    public Class<?> getFallback() {
        return this.fallback;
    }

    public void setFallback(Class<?> fallback) {
        this.fallback = fallback;
    }

    public Class<?> getFallbackFactory() {
        return this.fallbackFactory;
    }

    public void setFallbackFactory(Class<?> fallbackFactory) {
        this.fallbackFactory = fallbackFactory;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        } else if (o != null && this.getClass() == o.getClass()) {
            FeignClientFactoryBean that = (FeignClientFactoryBean)o;
            return Objects.equals(this.applicationContext, that.applicationContext) && this.decode404 == that.decode404 && Objects.equals(this.fallback, that.fallback) && Objects.equals(this.fallbackFactory, that.fallbackFactory) && Objects.equals(this.name, that.name) && Objects.equals(this.path, that.path) && Objects.equals(this.type, that.type) && Objects.equals(this.url, that.url);
        } else {
            return false;
        }
    }

    public int hashCode() {
        return Objects.hash(new Object[]{this.applicationContext, this.decode404, this.fallback, this.fallbackFactory, this.name, this.path, this.type, this.url});
    }

    public String toString() {
        return "FeignClientFactoryBean{" + "type=" + this.type + ", " + "name='" + this.name + "', " + "url='" + this.url + "', " + "path='" + this.path + "', " + "decode404=" + this.decode404 + ", " + "applicationContext=" + this.applicationContext + ", " + "fallback=" + this.fallback + ", " + "fallbackFactory=" + this.fallbackFactory + "}";
    }
}

首先:

1. org.springframework.cloud.openfeign.FeignClientFactoryBean#getObjectType 返回的類型是業務代碼向容器索要Bean判斷是否匹配的依據,其返回的是內部的type屬性。 這個屬性是在上面BeanDefinition內部維持的, 值夜就是我們聲明@FeignClient 注解的類名稱。

2. FeignClientFactoryBean 內部的屬性是在IoC容器啟動過程中創建完對象,然后屬性注入階段從BeanDefinition的propertyValues  中拿的。

 

接下來研究其getObject創建target對象過程中的主要操作。

 

org.springframework.cloud.openfeign.FeignClientFactoryBean實現了三個接口:FactoryBean<Object>, InitializingBean, ApplicationContextAware

FactoryBean 接口是生成對象; InitializingBean 是在屬性注入之后檢查contextId 和 name 屬性;ApplicationContextAware 是獲取ApplicationContext 對象工廠。注入FeignContext 的時機是在org.springframework.cloud.sleuth.instrument.web.client.feign.FeignContextBeanPostProcessor

getObject 調用getTarget 過程如下:

1. org.springframework.cloud.openfeign.FeignClientFactoryBean#feign 這里主要構造一個builder,主要包括如下操作:

構造了一個日志打印器(基於原來的接口創建的logger)、配置RequestInterceptor、Logger.Level、Retryer、QueryMapEncoder等屬性

這里有兩種配置方式吧,一種是基於Spring的Bean 注入的方式;一種是配置方式org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration(其中這種方式可以基於全局配置,也可以對某個服務單獨設置)

全局就是注入Bean的方式修改所有默認的,如果想單獨修改某個服務相關的可以在yml 進行配置:

feign:
  client:
    config:
      CLOUD-PAYMENT-SERVICE:
        loggerLevel: BASIC

3. 然后在沒有url 的情況下是按照服務名進行處理,拼接url 屬性為http://服務名稱。 如果有URL會按照URL的方式進行處理,並且如果URL沒有加http:// 會在這里加上,也就是URL 可以只寫域名加端口

4. 獲取一個feign.Target.HardCodedTarget 對象,這個對象里面實際就是一個記錄的功能,記錄了

type: 也就是借口的類型

name: 服務名稱

url: 地址信息

5. 調用org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance 方法

第一步獲取一個feign.Client 對象

第二步獲取一個org.springframework.cloud.openfeign.Targeter 對象

第三步調用方法 org.springframework.cloud.openfeign.Targeter#target 創建一個代理對象,這個方法邏輯如下

(1)調用方法org.springframework.cloud.openfeign.HystrixTargeter#target:

(2) 上面方法調用feign.Feign.Builder#target(feign.Target<T>)

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

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

  這里是創建一個工廠,然后調用下面方法

(3)feign.ReflectiveFeign#newInstance

    public <T> T newInstance(Target<T> target) {
        Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {
                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 = this.factory.create(target, methigeodToHandler);
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();

        while(var12.hasNext()) {
            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }

        return proxy;
    }

這里的重點就是獲取到一個methodToHandler對象,這個就是維護一個方法與隊應處理器的map,內容如下:

 然后調用feign.InvocationHandlerFactory.Default#create 創建InvocationHandler, 這個是JDK代理需要的參數。feign.ReflectiveFeign.FeignInvocationHandler 如下:

  static class FeignInvocationHandler implements InvocationHandler {

    private final Target target;
    private final Map<Method, MethodHandler> dispatch;

    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }

      return dispatch.get(method).invoke(args);
    }

    @Override
    public boolean equals(Object obj) {
      if (obj instanceof FeignInvocationHandler) {
        FeignInvocationHandler other = (FeignInvocationHandler) obj;
        return target.equals(other.target);
      }
      return false;
    }

    @Override
    public int hashCode() {
      return target.hashCode();
    }

    @Override
    public String toString() {
      return target.toString();
    }
  } 

接下來就是用Proxy.newProxyInstance 創建代理對象並且返回,這就是JDK的動態代理了。

補充:feign.ReflectiveFeign#newInstance 方法第一行是實例化過程中重要的一步,驗證方法的參數以及對參數對應注解的解析以及封裝也是在這一步

第一行代碼如下:

Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);

1. feign.ReflectiveFeign.ParseHandlersByName#apply  獲取方法與其對應的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, queryMapEncoder);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
        }
        result.put(md.configKey(),
            factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
      }
      return result;
    }

 2. feign.Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class<?>)  這里進行檢查方法參數、解析與封裝成List<MethodMetadata> 對象

    @Override
    public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
      checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s",
          targetType.getSimpleName());
      checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s",
          targetType.getSimpleName());
      if (targetType.getInterfaces().length == 1) {
        checkState(targetType.getInterfaces()[0].getInterfaces().length == 0,
            "Only single-level inheritance supported: %s",
            targetType.getSimpleName());
      }
      Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
      for (Method method : targetType.getMethods()) {
        if (method.getDeclaringClass() == Object.class ||
            (method.getModifiers() & Modifier.STATIC) != 0 ||
            Util.isDefault(method)) {
          continue;
        }
        MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
        checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s",
            metadata.configKey());
        result.put(metadata.configKey(), metadata);
      }
      return new ArrayList<>(result.values());
    }

(1) 首先進行了類合法性校驗:

泛型類是不被支持的

代理的類的接口數量為0個或者1個

如果有繼承的接口再判斷父接口的父接口是不是為空, 也就是只能實現單繼承

(2) 然后獲取到所有的方法進行遍歷解析獲得一個MethodMetadata 對象,主要的操作是:org.springframework.cloud.openfeign.support.SpringMvcContract#parseAndValidateMetadata

    public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
        this.processedMethods.put(Feign.configKey(targetType, method), method);
        MethodMetadata md = super.parseAndValidateMetadata(targetType, method);

        RequestMapping classAnnotation = findMergedAnnotation(targetType,
                RequestMapping.class);
        if (classAnnotation != null) {
            // produces - use from class annotation only if method has not specified this
            if (!md.template().headers().containsKey(ACCEPT)) {
                parseProduces(md, method, classAnnotation);
            }

            // consumes -- use from class annotation only if method has not specified this
            if (!md.template().headers().containsKey(CONTENT_TYPE)) {
                parseConsumes(md, method, classAnnotation);
            }

            // headers -- class annotation is inherited to methods, always write these if
            // present
            parseHeaders(md, method, classAnnotation);
        }
        return md;
    }

這里會調用父類方法:feign.Contract.BaseContract#parseAndValidateMetadata

    protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
      MethodMetadata data = new MethodMetadata();
      data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
      data.configKey(Feign.configKey(targetType, method));

      if (targetType.getInterfaces().length == 1) {
        processAnnotationOnClass(data, targetType.getInterfaces()[0]);
      }
      processAnnotationOnClass(data, targetType);


      for (Annotation methodAnnotation : method.getAnnotations()) {
        processAnnotationOnMethod(data, methodAnnotation, method);
      }
      checkState(data.template().method() != null,
          "Method %s not annotated with HTTP method type (ex. GET, POST)",
          method.getName());
      Class<?>[] parameterTypes = method.getParameterTypes();
      Type[] genericParameterTypes = method.getGenericParameterTypes();

      Annotation[][] parameterAnnotations = method.getParameterAnnotations();
      int count = parameterAnnotations.length;
      for (int i = 0; i < count; i++) {
        boolean isHttpAnnotation = false;
        if (parameterAnnotations[i] != null) {
          isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
        }
        if (parameterTypes[i] == URI.class) {
          data.urlIndex(i);
        } else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {
          checkState(data.formParams().isEmpty(),
              "Body parameters cannot be used with form parameters.");
          checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method);
          data.bodyIndex(i);
          data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
        }
      }

      if (data.headerMapIndex() != null) {
        checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()],
            genericParameterTypes[data.headerMapIndex()]);
      }

      if (data.queryMapIndex() != null) {
        if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
          checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
        }
      }

      return data;
    }

1》初始化一個 MethodMetadata 之后, Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 獲取一個方法參數對應的注解數組,二維數組是每一行代表對應的參數位置

比如下面方法:

    /**
     * 注意
     * 1. PathVariable 的value 屬性必須有。 會在org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor#processArgument(org.springframework.cloud.openfeign.AnnotatedParameterProcessor.AnnotatedParameterContext, java.lang.annotation.Annotation, java.lang.reflect.Method) 進行驗證
     *
     * @param map
     * @param queryParam
     * @param id
     * @return
     */
    @PostMapping("/pay/testParam/{id}")
//    JSONResultUtil<Map<String, Object>> testParam(@RequestBody Map<String, Object> map, @RequestParam("queryParam") String queryParam, @PathVariable("id") String id);
    // 下面這種寫可以, 參數上不寫任何注解默認會作為body 處理,但是body 只能出現一個, 多個會在啟動過程中檢查報錯
    JSONResultUtil<Map<String, Object>> testParam(Map<String, Object> map, @RequestParam("queryParam") String queryParam, @PathVariable("id") String id);

獲取到的數組為:

 2》然后循環處理方法的參數

  org.springframework.cloud.openfeign.support.SpringMvcContract#processAnnotationsOnParameter

    protected boolean processAnnotationsOnParameter(MethodMetadata data,
            Annotation[] annotations, int paramIndex) {
        boolean isHttpAnnotation = false;

        AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
                data, paramIndex);
        Method method = this.processedMethods.get(data.configKey());
        for (Annotation parameterAnnotation : annotations) {
            AnnotatedParameterProcessor processor = this.annotatedArgumentProcessors
                    .get(parameterAnnotation.annotationType());
            if (processor != null) {
                Annotation processParameterAnnotation;
                // synthesize, handling @AliasFor, while falling back to parameter name on
                // missing String #value():
                processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
                        parameterAnnotation, method, paramIndex);
                isHttpAnnotation |= processor.processArgument(context,
                        processParameterAnnotation, method);
            }
        }

        if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
            TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
            if (this.conversionService.canConvert(typeDescriptor,
                    STRING_TYPE_DESCRIPTOR)) {
                Param.Expander expander = this.convertingExpanderFactory
                        .getExpander(typeDescriptor);
                if (expander != null) {
                    data.indexToExpander().put(paramIndex, expander);
                }
            }
        }
        return isHttpAnnotation;
    }

  根據參數對應的注解數組信息 annotations 進行解析,根據注解類型從annotatedArgumentProcessors獲取到對應的參數解析器,然后調用processor.processArgument(context, processParameterAnnotation, method);  進行處理。

比如默認的注解類型與參數解析器關系如下:

  這里會進行參數的注解驗證。 默認什么注解也不寫會作為RequestBody 解析,也就是獲取到對應參數的順序標記為bodyIndex,用於后面方法調用時解析body。(這里注意最多有一個RequestBody, 如果多個會在checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method); 驗證時報錯)

  經過上面處理獲取到的MethodMetadata如下:

 3. 然后根據方法創建MethodHandler 對象存入Map 中返回去供使用

    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, closeAfterDecode, propagationPolicy);
    }

  到此,Feign 代理注冊以及對象創建完成。所以調用過程中的就是feign.ReflectiveFeign.FeignInvocationHandler#invoke方法。

4. 調用過程分析

 基於JDK的動態代理其入口是在InvocationHandler的invoke方法,上面是:feign.ReflectiveFeign.FeignInvocationHandler#invoke

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }

      return dispatch.get(method).invoke(args);
    }

  可以看到toString、equals、hashcode方法特殊處理,指揮就是根據Method 對象獲取到對應的MethodHandler 進行調用。

1. 調用到feign.SynchronousMethodHandler#invoke

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

(1) RequestTemplate.from(metadata.template());    根據上面創建對象過程中解析出來的RequestTemplate克隆一個RequestTemplate 

(2) 將參數解析出來,存放到: varBuilder

(3) resolve(argv, mutable, varBuilder); 根據解析出的參數替換url 中的變量以及queries 中需要的變量以及設置一些請求頭

 (4) feign.SynchronousMethodHandler#executeAndDecode 開始處理

  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      response = client.execute(request, options);
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

    boolean shouldClose = true;
    try {
      if (logLevel != Logger.Level.NONE) {
        response =
            logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
      }
      if (Response.class == metadata.returnType()) {
        if (response.body() == null) {
          return response;
        }
        if (response.body().length() == null ||
            response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
          shouldClose = false;
          return response;
        }
        // Ensure the response body is disconnected
        byte[] bodyData = Util.toByteArray(response.body().asInputStream());
        return response.toBuilder().body(bodyData).build();
      }
      if (response.status() >= 200 && response.status() < 300) {
        if (void.class == metadata.returnType()) {
          return null;
        } else {
          Object result = decode(response);
          shouldClose = closeAfterDecode;
          return result;
        }
      } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
        Object result = decode(response);
        shouldClose = closeAfterDecode;
        return result;
      } else {
        throw errorDecoder.decode(metadata.configKey(), response);
      }
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
      }
      throw errorReading(request, response, e);
    } finally {
      if (shouldClose) {
        ensureClosed(response.body());
      }
    }
  }

第一步: 這里首先調用這個方法對FeignInteceptor 攔截器做處理 feign.SynchronousMethodHandler#targetRequest, 並且將信息封裝到feign.Request 類中

  Request targetRequest(RequestTemplate template) {
    for (RequestInterceptor interceptor : requestInterceptors) {
      interceptor.apply(template);
    }
    return target.apply(template);
  }

第二步:打印request 對象

第三步:org.springframework.cloud.sleuth.instrument.web.client.feign.TraceLoadBalancerFeignClient#execute 調用

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("Before send");
        }
        Response response = null;
        Span fallbackSpan = tracer().nextSpan().start();
        try {
            response = super.execute(request, options);
            if (log.isDebugEnabled()) {
                log.debug("After receive");
            }
            return response;
        }
        catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("Exception thrown", e);
            }
            if (e instanceof IOException || e.getCause() != null
                    && e.getCause() instanceof ClientException
                    && ((ClientException) e.getCause())
                            .getErrorType() == ClientException.ErrorType.GENERAL) {
                if (log.isDebugEnabled()) {
                    log.debug(
                            "General exception was thrown, so most likely the traced client wasn't called. Falling back to a manual span");
                }
                fallbackSpan = tracingFeignClient().handleSend(
                        new HashMap<>(request.headers()), request, fallbackSpan);
                tracingFeignClient().handleReceive(fallbackSpan, response, e);
            }
            throw e;
        }
        finally {
            fallbackSpan.abandon();
        }
    }

轉交給父類org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute

    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);

            IClientConfig requestConfig = getClientConfig(options, clientName);
            return lbClient(clientName)
                    .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }

 

  之后調用com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(S, com.netflix.client.config.IClientConfig)  , 也就是請求交給ribbon

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM