• 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

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    demo04-默认标签
    demo03-段落标签
    demo02-标题标签
    demo01-注释标签
    前端基础介绍
    xadmin的详细使用
    设置Linux环境变量中文显示乱码
    ES应用
    HTTP协议
    jboss
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/14568086.html
Copyright © 2020-2023  润新知