• OpenFeign(一)


    spring-cloud-openFeign源码深度解析

    如何使用spring cloud feign

     1 @SpringBootApplication
     2 @EnableFeignClients
     3 public class WebApplication {
     4 
     5     public static void main(String[] args) {
     6         SpringApplication.run(WebApplication.class, args);
     7     }
     8 
     9     @FeignClient("name")
    10     static interface NameService {
    11         @RequestMapping("/")
    12         public String getName();
    13     }
    14 }

    从官网的例子中,可以看出是通过注解驱动的,所以从注解开始看起。

    spring cloud feign是如何工作的

    Feign涉及了两个注解,一个是@EnableFeignClients,用来开启 Feign,另一个是@FeignClient,用来标记要用 Feign 来拦截的请求接口。

     1 @Retention(RetentionPolicy.RUNTIME)
     2 @Target(ElementType.TYPE)
     3 @Documented
     4 @Import(FeignClientsRegistrar.class)
     5 public @interface EnableFeignClients {
     6 
     7     String[] value() default {};
     8 
     9     String[] basePackages() default {};
    10 
    11     Class<?>[] basePackageClasses() default {};
    12 
    13     Class<?>[] defaultConfiguration() default {};
    14 
    15     Class<?>[] clients() default {};
    16 }
     1 @Target(ElementType.TYPE)
     2 @Retention(RetentionPolicy.RUNTIME)
     3 @Documented
     4 public @interface FeignClient {
     5 
     6     @AliasFor("name")
     7     String value() default "";
     8 
     9     @Deprecated
    10     String serviceId() default "";
    11 
    12     @AliasFor("value")
    13     String name() default "";
    14     
    15     String qualifier() default "";
    16 
    17     String url() default "";
    18 
    19     boolean decode404() default false;
    20 
    21     Class<?>[] configuration() default {};
    22 
    23     Class<?> fallback() default void.class;
    24 
    25     Class<?> fallbackFactory() default void.class;
    26 
    27     String path() default "";
    28 
    29     boolean primary() default true;
    30 
    31 }

    @EnableFeignClients 是关于注解扫描的配置,比如扫描路径,配置等。@FeignClient 则是关于对该接口进行代理的时候,一些实现细节的配置,比如访问url是什么, fallback 方法,关于404的请求是抛错误还是正常返回。

    注册client

    先关注对EnableFeignClients 的处理,可以看出它使用了@Import(FeignClientsRegistrar.class),看名字可知是一个注册器,通过扫描某个特性的类,将bean注册到IOC中。Spring 通过调用其 registerBeanDefinitions 方法来获取其提供的 bean definition。

    1 public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
    2     //注册configuration
    3     registerDefaultConfiguration(metadata, registry);
    4     //注册注解
    5     registerFeignClients(metadata, registry);
    6 }
     1 private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
     2     //获取注解@EnableFeignClients 下设置的属性值
     3     Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
     4 
     5     if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
     6         String name;
     7         //判断传入的defaultConfiguration的是不是topClass,所谓topClass就是说此类不是别的类的内部类
     8         if (metadata.hasEnclosingClass()) {
     9             name = "default." + metadata.getEnclosingClassName();
    10         }
    11         else {
    12             name = "default." + metadata.getClassName();
    13         }
    14         registerClientConfiguration(registry, name,defaultAttrs.get("defaultConfiguration"));
    15     }
    16 }
    1 private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {
    2     //加载FeignClientSpecification bean
    3     BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
    4     builder.addConstructorArgValue(name);
    5     builder.addConstructorArgValue(configuration);
    6     //注册
    7     registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());
    8 }

         这里会往 Registry 里面添加一个BeanDefinition,即 FeignClientSpecification,configuration是通过 EnableFeignClients 注解的 defaultConfiguration 参数传入。

     1 public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
     2     ClassPathScanningCandidateComponentProvider scanner = getScanner();
     3     scanner.setResourceLoader(this.resourceLoader);
     4 
     5     Set<String> basePackages;
     6 
     7     Map<String, Object> attrs = metadata
     8         .getAnnotationAttributes(EnableFeignClients.class.getName());
     9     // 扫描带有FeignClient注解的类
    10     AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
    11         FeignClient.class);
    12     //获取@EnableFeignClients 中clients的值
    13     final Class<?>[] clients = attrs == null ? null
    14         : (Class<?>[]) attrs.get("clients");
    15     if (clients == null || clients.length == 0) {
    16         //如果没有设置,那么加入要扫描的注解和扫描的包
    17         scanner.addIncludeFilter(annotationTypeFilter);
    18         // 确定扫描的包路径列表
    19         basePackages = getBasePackages(metadata);
    20     }
    21     else {
    22         //如果设置了,最终扫出来的Bean必须是注解中设置的那些
    23         final Set<String> clientClasses = new HashSet<>();
    24         basePackages = new HashSet<>();
    25         for (Class<?> clazz : clients) {
    26             basePackages.add(ClassUtils.getPackageName(clazz));
    27             clientClasses.add(clazz.getCanonicalName());
    28         }
    29         AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
    30             @Override
    31             protected boolean match(ClassMetadata metadata) {
    32                 String cleaned = metadata.getClassName().replaceAll("\$", ".");
    33                 return clientClasses.contains(cleaned);
    34             }
    35         };
    36         scanner.addIncludeFilter(
    37             new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
    38     }
    39     //循环扫描,并把根据注解信息,进行相关注册
    40     for (String basePackage : basePackages) {
    41         Set<BeanDefinition> candidateComponents = scanner
    42             .findCandidateComponents(basePackage);
    43         for (BeanDefinition candidateComponent : candidateComponents) {
    44             if (candidateComponent instanceof AnnotatedBeanDefinition) {
    45                 // verify annotated class is an interface
    46                 AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
    47                 AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
    48                 //必须注解在interface上
    49                 Assert.isTrue(annotationMetadata.isInterface(),
    50                               "@FeignClient can only be specified on an interface");
    51 
    52                 Map<String, Object> attributes = annotationMetadata
    53                     .getAnnotationAttributes(
    54                     FeignClient.class.getCanonicalName());
    55 
    56                 String name = getClientName(attributes);
    57                 registerClientConfiguration(registry, name,attributes.get("configuration"));
    58 
    59                 registerFeignClient(registry, annotationMetadata, attributes);
    60             }
    61         }
    62     }
    63 }
    64 
    65 private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    66     String className = annotationMetadata.getClassName();
    67     BeanDefinitionBuilder definition = BeanDefinitionBuilder
    68         .genericBeanDefinition(FeignClientFactoryBean.class);
    69     validate(attributes);
    70     //将属性设置到FeignClientFactoryBean 中
    71     definition.addPropertyValue("url", getUrl(attributes));
    72     definition.addPropertyValue("path", getPath(attributes));
    73     String name = getName(attributes);
    74     definition.addPropertyValue("name", name);
    75     definition.addPropertyValue("type", className);
    76     definition.addPropertyValue("decode404", attributes.get("decode404"));
    77     definition.addPropertyValue("fallback", attributes.get("fallback"));
    78     definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    79     //设置Autowire 类型
    80     definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    81 
    82     String alias = name + "FeignClient";
    83     AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    84 
    85     boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
    86 
    87     beanDefinition.setPrimary(primary);
    88 
    89     String qualifier = getQualifier(attributes);
    90     if (StringUtils.hasText(qualifier)) {
    91         alias = qualifier;
    92     }
    93     //注册bean
    94     BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
    95                                                            new String[] { alias });
    96     BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    97 }

    另一个往 Registry 里面添加的 BeanDefinition则是FeignClientFactoryBean,负责注册FeignClient。

    也就是说,Feign的注册一共分为一下几步:

    1. 扫描@EnableFeignClients注解,如果有defaultConfiguration属性配置,则将configuration注册到BeanDefinition中,如果不指定的话,spring 提供的默认配置是FeignClientsConfiguration。
    2. 扫描 basePackage 下面所有包含了 @FeignClient 注解的类
    3. 如果@EnableFeignClients中配置了clients属性,则扫描出来的bean只有在clients中配置的那些
    4. 循环扫描@FeignClient注解,如果配置了configuration,则将configuration按照 1 注册打BeanDefinition中,也就是说Feign既支持用作统一的默认的Config作为全局配置,也可以分别在@FeignClient中单独配置configuration 作为局部配置。
    5. 将@FeignClient中的其他配置设置到FeignClientFactoryBean中。
    6. 最后调用FeignClientFactoryBean#getObject来创建client实例。

    加载配置项

     接下来来看下 FeignClientFactoryBean,Spring Context 创建 Bean 实例时会调用它的 getObject 方法。

     1 public Object getObject() throws Exception {
     2     return getTarget();
     3 }
     4 
     5 /**
     6 * @param <T> the target type of the Feign client
     7 * @return a {@link Feign} client created with the specified data and the context information
     8 */
     9 <T> T getTarget() {
    10     FeignContext context = applicationContext.getBean(FeignContext.class);
    11     Feign.Builder builder = feign(context);//1
    12 
    13     if (!StringUtils.hasText(this.url)) {
    14         //如果没有指定url,获取name值拼接默认url
    15         String url;
    16         if (!this.name.startsWith("http")) {
    17             url = "http://" + this.name;
    18         }
    19         else {
    20             url = this.name;
    21         }
    22         url += cleanPath();
    23         return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
    24                                                                        this.name, url));
    25     }
    26     if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
    27         this.url = "http://" + this.url;
    28     }
    29     String url = this.url + cleanPath();
    30     Client client = getOptional(context, Client.class);
    31     if (client != null) {
    32         if (client instanceof LoadBalancerFeignClient) {
    33             //使用ribbon提供的负载均衡
    34             // not load balancing because we have a url,
    35             // but ribbon is on the classpath, so unwrap
    36             client = ((LoadBalancerFeignClient)client).getDelegate();
    37         }
    38         builder.client(client);
    39     }
    40     Targeter targeter = get(context, Targeter.class);
    41     return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
    42         this.type, this.name, url));
    43 }
    • 如果未指定url,则根据client的name来拼接url,并开启负载均衡
    • 如果指定了URL,没有指定client,那么就根据url来调用,相当于直连,没有负载均衡。如果没有指定client的话,可以使用负载均衡。现在的版本是默认开启负载均衡。

    首先在1处可以看出通过feign(context)方法初始化了Feign.Builder,所以着重看一下这个方法:

     1 protected Feign.Builder feign(FeignContext context) {
     2     //获取FeignClientsConfiguration 中注册的bean ,设置到feign中
     3     FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
     4     Logger logger = loggerFactory.create(this.type);
     5 
     6     // @formatter:off
     7     Feign.Builder builder = get(context, Feign.Builder.class)
     8         // required values
     9         .logger(logger)
    10         .encoder(get(context, Encoder.class))
    11         .decoder(get(context, Decoder.class))
    12         .contract(get(context, Contract.class));
    13     // @formatter:on
    14 
    15     configureFeign(context, builder);
    16 
    17     return builder;
    18 }

     这里被设置到builder的bean来自于FeignClientsConfiguration在启动时加载到了context中,这是spring的默认配置,即使在@EnableFeignClients和@FeignClient没有配置configuration也能保证可以使用。

     1 @Configuration
     2 public class FeignClientsConfiguration {
     3     ...
     4         @Bean
     5         @ConditionalOnMissingBean
     6         public Decoder feignDecoder() {
     7         return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
     8     }
     9 
    10     @Bean
    11     @ConditionalOnMissingBean
    12     public Encoder feignEncoder() {
    13         return new SpringEncoder(this.messageConverters);
    14     }
    15 
    16     @Bean
    17     @ConditionalOnMissingBean
    18     public Contract feignContract(ConversionService feignConversionService) {
    19         return new SpringMvcContract(this.parameterProcessors, feignConversionService);
    20     }
    21 
    22     @Bean
    23     @Scope("prototype")
    24     @ConditionalOnMissingBean
    25     public Feign.Builder feignBuilder(Retryer retryer) {
    26         return Feign.builder().retryer(retryer);
    27     }
    28     ...
    29 }

    那么自定义的configuration在哪里加载呢,可以看到方法feign(context)中最后一行调用了configureFeign(context, builder),来看一下这个方法。

      1 protected void configureFeign(FeignContext context, Feign.Builder builder) {
      2     //获取.properties的属性
      3     FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
      4     if (properties != null) {
      5         if (properties.isDefaultToProperties()) {
      6             //默认为true
      7             configureUsingConfiguration(context, builder);
      8             configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
      9             configureUsingProperties(properties.getConfig().get(this.name), builder);
     10         } else {
     11             configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
     12             configureUsingProperties(properties.getConfig().get(this.name), builder);
     13             configureUsingConfiguration(context, builder);
     14         }
     15     } else {
     16         configureUsingConfiguration(context, builder);
     17     }
     18 }
     19 //获取用户通过configuration @Bean的自定义配置
     20 protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
     21     Logger.Level level = getOptional(context, Logger.Level.class);
     22     if (level != null) {
     23         builder.logLevel(level);
     24     }
     25     Retryer retryer = getOptional(context, Retryer.class);
     26     if (retryer != null) {
     27         builder.retryer(retryer);
     28     }
     29     ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
     30     if (errorDecoder != null) {
     31         builder.errorDecoder(errorDecoder);
     32     }
     33     //connectTimeoutMillis和readTimeoutMillis的默认值
     34     Request.Options options = getOptional(context, Request.Options.class);
     35     if (options != null) {
     36         builder.options(options);
     37     }
     38     Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
     39         this.name, RequestInterceptor.class);
     40     if (requestInterceptors != null) {
     41         builder.requestInterceptors(requestInterceptors.values());
     42     }
     43 
     44     if (decode404) {
     45         builder.decode404();
     46     }
     47 }
     48 
     49 
     50 /***
     51      *
     52      * @param config 获取.properties中配置的bean
     53      * @param builder feign
     54      */
     55 protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) {
     56     if (config == null) {
     57         return;
     58     }
     59 
     60     if (config.getLoggerLevel() != null) {
     61         builder.logLevel(config.getLoggerLevel());
     62     }
     63     //设置connectTimeoutMillis和readTimeoutMillis的值,这里的属性值来自于.properties配置的
     64     if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
     65         builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
     66     }
     67 
     68     if (config.getRetryer() != null) {
     69         Retryer retryer = getOrInstantiate(config.getRetryer());
     70         builder.retryer(retryer);
     71     }
     72 
     73     if (config.getErrorDecoder() != null) {
     74         ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
     75         builder.errorDecoder(errorDecoder);
     76     }
     77 
     78     if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
     79         // this will add request interceptor to builder, not replace existing
     80         //这里只会往原有的interceptor中添加新的,而不会删掉原有的。
     81         for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
     82             RequestInterceptor interceptor = getOrInstantiate(bean);
     83             builder.requestInterceptor(interceptor);
     84         }
     85     }
     86 
     87     if (config.getDecode404() != null) {
     88         if (config.getDecode404()) {
     89             builder.decode404();
     90         }
     91     }
     92 
     93     if (Objects.nonNull(config.getEncoder())) {
     94         builder.encoder(getOrInstantiate(config.getEncoder()));
     95     }
     96 
     97     if (Objects.nonNull(config.getDecoder())) {
     98         builder.decoder(getOrInstantiate(config.getDecoder()));
     99     }
    100 
    101     if (Objects.nonNull(config.getContract())) {
    102         builder.contract(getOrInstantiate(config.getContract()));
    103     }
    104 }

    把配置文件中的配置项在启动时初始化到FeignClientProperties中。

    • 如果配置文件中没有配置,则将FeignClientsConfiguration中的bean作为默认值设置到builder。
    • 如果配置文件中有配置,并且用默认加载顺序时,首先加载FeignClientsConfiguration中的bean,然后加载在注解中配置的configuration,最后加载配置文件中的。
    • 如果不是默认加载顺序,则首先加载注解中配置的configuration,然后加载配置文件中的配置,最后加载FeignClientsConfiguration中的bean。注意,顺序在后面的配置会覆盖掉前面的

    创建client实例

    配置文件加载完之后,就是最关键的一步,创建实例.因为两种方式都是通过获取 Targeter 来生成动态代理类。这里拿出了负载均衡做例子。

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

     在 FeignAutoConfiguration 里面,配置了Target,可以看出这里配置了两种相斥的bean。

     1 @Configuration
     2 @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
     3 protected static class HystrixFeignTargeterConfiguration {
     4     @Bean
     5     @ConditionalOnMissingBean
     6     public Targeter feignTargeter() {
     7         return new HystrixTargeter();
     8     }
     9 }
    10 
    11 @Configuration
    12 @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    13 protected static class DefaultFeignTargeterConfiguration {
    14     @Bean
    15     @ConditionalOnMissingBean
    16     public Targeter feignTargeter() {
    17         return new DefaultTargeter();
    18     }
    19 }

    如果 feign.hystrix.HystrixFeign`路径不存在,则直接用 FeignBuidler 中DefaultTargeter的 target 方法生成代理。

     1 class HystrixTargeter implements Targeter {
     2 
     3     @Override
     4     public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,Target.HardCodedTarget<T> target) {
     5         if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
     6             return feign.target(target);
     7         }
     8         feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
     9         SetterFactory setterFactory = getOptional(factory.getName(), context,
    10                                                   SetterFactory.class);
    11         if (setterFactory != null) {
    12             builder.setterFactory(setterFactory);
    13         }
    14         Class<?> fallback = factory.getFallback();
    15         if (fallback != void.class) {
    16             return targetWithFallback(factory.getName(), context, target, builder, fallback);
    17         }
    18         Class<?> fallbackFactory = factory.getFallbackFactory();
    19         if (fallbackFactory != void.class) {
    20             return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
    21         }
    22 
    23         return feign.target(target);
    24     }
    25 }
    1 class DefaultTargeter implements Targeter {
    2 
    3     @Override
    4     public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,Target.HardCodedTarget<T> target) {
    5         return feign.target(target);
    6     }
    7 }

    到这里spring对于创建client实例工作基本完成。接下来主要步骤在feign中。 

    Feign是怎么工作的

    构建接口动态代理

    这里会直接调用Feign的target方法:

     1 public <T> T target(Target<T> target) {
     2     return build().newInstance(target);
     3 }
     4 
     5 private InvocationHandlerFactory invocationHandlerFactory =new InvocationHandlerFactory.Default();
     6 
     7 private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
     8 
     9 public Feign build() {
    10     SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
    11         new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
    12                                              logLevel, decode404, closeAfterDecode);
    13     //handlersByName将所有参数进行封装,并提供解析接口方法的逻辑
    14     ParseHandlersByName handlersByName =
    15         new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
    16                                 errorDecoder, synchronousMethodHandlerFactory);
    17     return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    18 }

    ReflectiveFeign构造函数有三个参数:

    • ParseHandlersByName 将builder所有参数进行封装,并提供解析接口方法的逻辑
    • InvocationHandlerFactory 默认值是InvocationHandlerFactory.Default,通过java动态代理的InvocationHandler实现
    • QueryMapEncoder 接口参数注解@QueryMap时,参数的编码器,默认值QueryMapEncoder.Default

    ReflectiveFeign 生成动态代理对象。

    ReflectiveFeign#newInstance

     1 public <T> T newInstance(Target<T> target) {
     2     //为每个方法创建一个SynchronousMethodHandler对象,并放在 Map 里面。
     3     //targetToHandlersByName是构造器传入的ParseHandlersByName对象,根据target对象生成MethodHandler映射
     4     Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
     5     Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
     6     List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
     7     //遍历接口所有方法,构建Method->MethodHandler的映射
     8     for (Method method : target.type().getMethods()) {
     9       if (method.getDeclaringClass() == Object.class) {
    10         continue;
    11       } else if(Util.isDefault(method)) {
    12         //如果是 default 方法,说明已经有实现了,用 DefaultHandler接口default方法的Handler
    13         DefaultMethodHandler handler = new DefaultMethodHandler(method);
    14         defaultMethodHandlers.add(handler);
    15         methodToHandler.put(method, handler);
    16       } else {
    17         //否则就用上面的 SynchronousMethodHandler
    18         methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    19       }
    20     }
    21     // 创建动态代理,factory 是 InvocationHandlerFactory.Default,创建出来的是 ReflectiveFeign.FeignInvocationHanlder,也就是说后续对方法的调用都会进入到该对象的 inovke 方法。
    22     InvocationHandler handler = factory.create(target, methodToHandler);
    23     // 创建动态代理对象
    24     T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
    25     //将default方法直接绑定到动态代理上
    26     for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    27       defaultMethodHandler.bindTo(proxy);
    28     }
    29     return proxy;
    30   }

    这段代码主要的逻辑是:

    1. 创建MethodHandler的映射,这里创建的是实现类SynchronousMethodHandler
    2. 通过InvocationHandlerFatory创建InvocationHandler
    3. 绑定接口的default方法,通过DefaultMethodHandler绑定

    SynchronousMethodHandler和DefaultMethodHandler实现了InvocationHandlerFactory.MethodHandler接口,动态代理对象调用方法时,如果是default方法,会直接调用接口方法,因为这里将接口的default方法绑定到动态代理对象上了,其他方法根据方法签名找到SynchronousMethodHandler对象,调用其invoke方法。

    创建MethodHandler方法处理器

    ReflectiveFeign#apply

     1 public Map<String, MethodHandler> apply(Target key) {
     2     //通过contract解析接口方法,生成MethodMetadata列表,默认的contract解析Feign自定义的http注解
     3     List<MethodMetadata> metadata =contract.parseAndValidatateMetadata(key.type());
     4     Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
     5     for (MethodMetadata md : metadata) {
     6         //根据目标接口类和方法上的注解信息判断该用哪种 buildTemplate
     7         //BuildTemplateByResolvingArgs实现RequestTemplate.Factory,RequestTemplate的工厂
     8         BuildTemplateByResolvingArgs buildTemplate;
     9         if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
    10             //如果有formParam,并且bodyTemplate不为空,请求体为x-www-form-urlencoded格式
    11             //将会解析form参数,填充到bodyTemplate中
    12             buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
    13         } else if (md.bodyIndex() != null) {
    14             //如果包含请求体,将会用encoder编码请求体对象
    15             buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
    16         } else {
    17             //默认的RequestTemplate的工厂,没有请求体,不需要编码器
    18             buildTemplate = new BuildTemplateByResolvingArgs(md);
    19         }
    20         //使用工厂SynchronousMethodHandler.Factory创建SynchronousMethodHandler
    21         result.put(md.configKey(),
    22                    factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
    23     }
    24     return result;
    25 }

    这段代码的逻辑是:

    1. 通过Contract解析接口方法,生成MethodMetadata,默认的Contract解析Feign自定义的http注解
    2. 根据MethodMetadata方法元数据生成特定的RequestTemplate的工厂
    3. 使用SynchronousMethodHandler.Factory工厂创建SynchronousMethodHandler,这里有两个工厂不要搞混淆了,SynchronousMethodHandler工厂和RequestTemplate工厂,SynchronousMethodHandler的属性包含RequestTemplate工厂

    SynchronousMethodHandlerFactory#create

    1 public MethodHandler create(Target<?> target, MethodMetadata md,
    2                             RequestTemplate.Factory buildTemplateFromArgs,
    3                             Options options, Decoder decoder, ErrorDecoder errorDecoder) {
    4     //buildTemplateFromArgs--RequestTemplate Facoory
    5     return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
    6                                         logLevel, md, buildTemplateFromArgs, options, decoder,
    7                                         errorDecoder, decode404);
    8 }

    Contract解析接口方法生成MethodMetadata

    这里有两个Contract,一个是feign默认的Contract,另一个是Spring实现的Contract。

    Feign.Contract-Contract.Default

    feign默认的解析器是Contract.Default继承了Contract.BaseContract,解析生成MethodMetadata方法入口:

     1 @Override
     2 public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
     3 
     4     Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
     5     for (Method method : targetType.getMethods()) {
     6         //...
     7         MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
     8         //...
     9         result.put(metadata.configKey(), metadata);
    10     }
    11     return new ArrayList<MethodMetadata>(result.values());
    12 }
    13 
    14 protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
    15     MethodMetadata data = new MethodMetadata();
    16     data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
    17     data.configKey(Feign.configKey(targetType, method));
    18 
    19     if(targetType.getInterfaces().length == 1) {
    20         processAnnotationOnClass(data, targetType.getInterfaces()[0]);
    21     }
    22     //处理Class上的注解
    23     processAnnotationOnClass(data, targetType);
    24 
    25     for (Annotation methodAnnotation : method.getAnnotations()) {
    26         //处理方法注解
    27         processAnnotationOnMethod(data, methodAnnotation, method);
    28     }
    29     //...
    30     Class<?>[] parameterTypes = method.getParameterTypes();
    31     Type[] genericParameterTypes = method.getGenericParameterTypes();
    32     
    33     Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    34     int count = parameterAnnotations.length;
    35     for (int i = 0; i < count; i++) {
    36         boolean isHttpAnnotation = false;
    37         if (parameterAnnotations[i] != null) {
    38             //方法参数注解
    39             isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
    40         }
    41         if (parameterTypes[i] == URI.class) {
    42             //参数类型是URI,后面构造http请求时,使用该URI
    43             data.urlIndex(i);
    44         } else if (!isHttpAnnotation) {
    45             //如果没有被http注解,就是body参数
    46             //...
    47             data.bodyIndex(i);
    48             data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
    49         }
    50     }
    51 
    52     if (data.headerMapIndex() != null) {
    53         //@HeaderMap注解的参数必须是Map,key类型必须是String
    54         checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]);
    55     }
    56 
    57     if (data.queryMapIndex() != null) {
    58         if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
    59             //@QueryMap注解的参数如果是Map,key类型必须是String
    60             checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
    61         }
    62     }
    63     return data;
    64 }

    1.处理Class上的注解

    这里主要处理@Headers

     1 //处理@Headers注解
     2 protected void processAnnotationOnClass(MethodMetadata data, Class<?> targetType) {
     3     if (targetType.isAnnotationPresent(Headers.class)) {
     4         //被Headers注解
     5         String[] headersOnType = targetType.getAnnotation(Headers.class).value();
     6         //...
     7         //header解析成map,加到MethodMetadata中
     8         Map<String, Collection<String>> headers = toMap(headersOnType);
     9         headers.putAll(data.template().headers());
    10         data.template().headers(null); // to clear
    11         data.template().headers(headers);
    12     }
    13 }

    2.处理方法上的注解

    这里主要处理@RequestLine@Body@Headers 

     1 //处理方法注解
     2 protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation,
     3                                          Method method) {
     4     Class<? extends Annotation> annotationType = methodAnnotation.annotationType();
     5     if (annotationType == RequestLine.class) {
     6         //@RequestLine注解
     7         String requestLine = RequestLine.class.cast(methodAnnotation).value();
     8         //...
     9         if (requestLine.indexOf(' ') == -1) {
    10             //...
    11             data.template().method(requestLine);
    12             return;
    13         }
    14         //http请求方法
    15         data.template().method(requestLine.substring(0, requestLine.indexOf(' ')));
    16         if (requestLine.indexOf(' ') == requestLine.lastIndexOf(' ')) {
    17             // no HTTP version is ok
    18             data.template().append(requestLine.substring(requestLine.indexOf(' ') + 1));
    19         } else {
    20             // skip HTTP version
    21             data.template().append(
    22                 requestLine.substring(requestLine.indexOf(' ') + 1, requestLine.lastIndexOf(' ')));
    23         }
    24         //将'%2F'反转为'/'
    25         data.template().decodeSlash(RequestLine.class.cast(methodAnnotation).decodeSlash());
    26         //参数集合格式化方式,默认使用key=value0&key=value1
    27         data.template().collectionFormat(RequestLine.class.cast(methodAnnotation).collectionFormat());
    28 
    29     } else if (annotationType == Body.class) {
    30         //@Body注解
    31         String body = Body.class.cast(methodAnnotation).value();
    32         //...
    33         if (body.indexOf('{') == -1) {
    34             //body中不存在{,直接传入body
    35             data.template().body(body);
    36         } else {
    37             //body中存在{,就是bodyTemplate方式
    38             data.template().bodyTemplate(body);
    39         }
    40     } else if (annotationType == Headers.class) {
    41         //@Header注解
    42         String[] headersOnMethod = Headers.class.cast(methodAnnotation).value();
    43         //...
    44         data.template().headers(toMap(headersOnMethod));
    45     }
    46 }

    3.处理参数上的注解

    这里主要处理@Param@QueryMap@HeaderMap,只要有这三个注解,则isHttpAnnotation=true。

     1 //处理参数上的注解
     2 protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
     3     boolean isHttpAnnotation = false;
     4     for (Annotation annotation : annotations) {
     5         Class<? extends Annotation> annotationType = annotation.annotationType();
     6         if (annotationType == Param.class) {
     7             //@Param注解
     8             Param paramAnnotation = (Param) annotation;
     9             String name = paramAnnotation.value();
    10             //...
    11             //增加到MethodMetadata中
    12             nameParam(data, name, paramIndex);
    13             //@Param注解的expander参数,定义参数的解释器,默认是ToStringExpander,调用参数的toString方法
    14             Class<? extends Param.Expander> expander = paramAnnotation.expander();
    15             if (expander != Param.ToStringExpander.class) {
    16                 data.indexToExpanderClass().put(paramIndex, expander);
    17             }
    18             //参数是否已经urlEncoded,如果没有,会使用urlEncoded方式编码
    19             data.indexToEncoded().put(paramIndex, paramAnnotation.encoded());
    20             isHttpAnnotation = true;
    21             String varName = '{' + name + '}';
    22             if (!data.template().url().contains(varName) &&
    23                 !searchMapValuesContainsSubstring(data.template().queries(), varName) &&
    24                 !searchMapValuesContainsSubstring(data.template().headers(), varName)) {
    25                 //如果参数不在path里面,不在query里面,不在header里面,就设置到formParam中
    26                 data.formParams().add(name);
    27             }
    28         } else if (annotationType == QueryMap.class) {
    29             //@QueryMap注解,注解参数对象时,将该参数转换为http请求参数格式发送
    30             //...
    31             data.queryMapIndex(paramIndex);
    32             data.queryMapEncoded(QueryMap.class.cast(annotation).encoded());
    33             isHttpAnnotation = true;
    34         } else if (annotationType == HeaderMap.class) {
    35             //@HeaderMap注解,注解一个Map类型的参数,放入http header中发送
    36             //...
    37             data.headerMapIndex(paramIndex);
    38             isHttpAnnotation = true;
    39         }
    40     }
    41     return isHttpAnnotation;
    42 }

    Spring.Contract-SpringMvcContract

     feign默认的解析器是SpringMvcContract继承了Contract.BaseContract,解析生成MethodMetadata方法入口:

     1 @Override
     2 public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
     3     this.processedMethods.put(Feign.configKey(targetType, method), method);
     4     MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
     5 
     6     RequestMapping classAnnotation = findMergedAnnotation(targetType,
     7                                                           RequestMapping.class);
     8     if (classAnnotation != null) {
     9         // produces - use from class annotation only if method has not specified this
    10         // 如果Accept为空,则设置@RequestMapping的produces
    11         if (!md.template().headers().containsKey(ACCEPT)) {
    12             parseProduces(md, method, classAnnotation);
    13         }
    14 
    15         // consumes -- use from class annotation only if method has not specified this
    16         // 如果Content-Type为空,则设置@RequestMapping的consumes
    17         if (!md.template().headers().containsKey(CONTENT_TYPE)) {
    18             parseConsumes(md, method, classAnnotation);
    19         }
    20 
    21         // headers -- class annotation is inherited to methods, always write these if
    22         // present 设置heerders
    23         parseHeaders(md, method, classAnnotation);
    24     }
    25     return md;
    26 }

    这里parseAndValidateMetadata调用了父类即BaseContract的方法,如上,也会分为3部来处理,这里只分析有区别的部分。解析完成之后,会根据在class上注解的@RequestMapping的属性值来设置到MethodMetadata中。

    1.处理Class上的注解

    这里主要处理@RequestMapping 

     1 protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
     2     if (clz.getInterfaces().length == 0) {
     3         RequestMapping classAnnotation = findMergedAnnotation(clz,
     4                                                               RequestMapping.class);
     5         if (classAnnotation != null) {
     6             // Prepend path from class annotation if specified
     7             if (classAnnotation.value().length > 0) {
     8                 String pathValue = emptyToNull(classAnnotation.value()[0]);
     9                 pathValue = resolve(pathValue);
    10                 if (!pathValue.startsWith("/")) {
    11                     pathValue = "/" + pathValue;
    12                 }
    13                 //插入url
    14                 data.template().insert(0, pathValue);
    15             }
    16         }
    17     }
    18 }

    2.处理方法上的注解

     这里主要处理@RequestMapping,和处理class时差不多

     1 protected void processAnnotationOnMethod(MethodMetadata data,
     2                                          Annotation methodAnnotation, Method method) {
     3     if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
     4         .annotationType().isAnnotationPresent(RequestMapping.class)) {
     5         return;
     6     }
     7 
     8     RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
     9     // 设置HTTP Method
    10     RequestMethod[] methods = methodMapping.method();
    11     if (methods.length == 0) {
    12         methods = new RequestMethod[] { RequestMethod.GET };
    13     }
    14     checkOne(method, methods, "method");
    15     data.template().method(methods[0].name());
    16 
    17     // path
    18     checkAtMostOne(method, methodMapping.value(), "value");
    19     if (methodMapping.value().length > 0) {
    20         String pathValue = emptyToNull(methodMapping.value()[0]);
    21         if (pathValue != null) {
    22             pathValue = resolve(pathValue);
    23             // Append path from @RequestMapping if value is present on method
    24             if (!pathValue.startsWith("/")
    25                 && !data.template().toString().endsWith("/")) {
    26                 pathValue = "/" + pathValue;
    27             }
    28             data.template().append(pathValue);
    29         }
    30     }
    31 
    32     // produces
    33     parseProduces(data, method, methodMapping);
    34 
    35     // consumes
    36     parseConsumes(data, method, methodMapping);
    37 
    38     // headers
    39     parseHeaders(data, method, methodMapping);
    40 
    41     data.indexToExpander(new LinkedHashMap<Integer, Param.Expander>());
    42 }

    3.处理参数上的注解

    这里主要处理@Param@QueryMap@HeaderMap

     1 protected boolean processAnnotationsOnParameter(MethodMetadata data,
     2                                                 Annotation[] annotations, int paramIndex) {
     3     boolean isHttpAnnotation = false;
     4 
     5     AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
     6         data, paramIndex);
     7     Method method = this.processedMethods.get(data.configKey());
     8     for (Annotation parameterAnnotation : annotations) {
     9         AnnotatedParameterProcessor processor = this.annotatedArgumentProcessors
    10             .get(parameterAnnotation.annotationType());
    11         if (processor != null) {
    12             Annotation processParameterAnnotation;
    13             // synthesize, handling @AliasFor, while falling back to parameter name on
    14             // missing String #value():
    15             processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
    16                 parameterAnnotation, method, paramIndex);
    17             //调用不同策略处理不同的注解
    18             isHttpAnnotation |= processor.processArgument(context,
    19                                                           processParameterAnnotation, method);
    20         }
    21     }
    22     if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null
    23         && this.conversionService.canConvert(
    24             method.getParameterTypes()[paramIndex], String.class)) {
    25         data.indexToExpander().put(paramIndex, this.expander);
    26     }
    27     return isHttpAnnotation;
    28 }

    这里spring提供了三种参数上的注解解析

    • RequestHeaderParameterProcessor:处理@RequestHeader
    • PathVariableParameterProcessor:处理@PathVariable
    • RequestParamParameterProcessor:处理@RequestParam

    代码稍微有点多,但是逻辑很清晰,先处理类上的注解,再处理方法上注解,最后处理方法参数注解,把所有注解的情况都处理到就可以了。

    生成的MethodMetadata的结构如下:

     1 public final class MethodMetadata implements Serializable {
     2     //标识方法的key,接口名加方法签名:GitHub#contributors(String,String)
     3     private String configKey;
     4     //方法返回值类型
     5     private transient Type returnType;
     6     //uri参数的位置,方法中可以写个uri参数,发请求时直接使用这个参数
     7     private Integer urlIndex;
     8     //body参数的位置,只能有一个注解的参数为body,否则报错
     9     private Integer bodyIndex;
    10     //headerMap参数的位置
    11     private Integer headerMapIndex;
    12     //@QueryMap注解参数位置
    13     private Integer queryMapIndex;
    14     //@QueryMap注解里面encode参数,是否已经urlEncode编码过了
    15     private boolean queryMapEncoded;
    16     //body的类型
    17     private transient Type bodyType;
    18     //RequestTemplate
    19     private RequestTemplate template = new RequestTemplate();
    20     //form请求参数
    21     private List<String> formParams = new ArrayList<String>();
    22     //方法参数位置和名称的map
    23     private Map<Integer, Collection<String>> indexToName ;
    24     //@Param中注解的expander方法,可以指定解析参数类
    25     private Map<Integer, Class<? extends Expander>> indexToExpanderClass ;
    26     //参数是否被urlEncode编码过了,@Param中encoded方法
    27     private Map<Integer, Boolean> indexToEncoded ;
    28     //自定义的Expander
    29     private transient Map<Integer, Expander> indexToExpander;

    调用

    ReflectiveFeign.FeignInvocationHanlder 的 invoke 方法

     1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     2     //通过动态代理实现了几个通用方法,比如 equals、toString、hasCode
     3     if ("equals".equals(method.getName())) {
     4         try {
     5             Object
     6                 otherHandler =
     7                 args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
     8             return equals(otherHandler);
     9         } catch (IllegalArgumentException e) {
    10             return false;
    11         }
    12     } else if ("hashCode".equals(method.getName())) {
    13         return hashCode();
    14     } else if ("toString".equals(method.getName())) {
    15         return toString();
    16     }
    17     //找到具体的 method 的 Handler,然后调用 invoke 方法。这样就又进入了SynchronousMethodHandler对象的 invoke 方法。
    18     return dispatch.get(method).invoke(args);
    19 }

    SynchronousMethodHandler 的 invoke 方法主要是应用 encoder,decoder 以及 retry 等配置, 并执行http请求及返回结果的处理

     1 public Object invoke(Object[] argv) throws Throwable {
     2     RequestTemplate template = buildTemplateFromArgs.create(argv);
     3     Retryer retryer = this.retryer.clone();
     4     while (true) {
     5         try {
     6             return executeAndDecode(template);
     7         } catch (RetryableException e) {
     8             retryer.continueOrPropagate(e);
     9             if (logLevel != Logger.Level.NONE) {
    10                 logger.logRetry(metadata.configKey(), logLevel);
    11             }
    12             continue;
    13         }
    14     }
    15 }
    16 
    17 Object executeAndDecode(RequestTemplate template) throws Throwable {
    18     //通过RequestTemplate生成Request,这里会首先执行RequestInterceptors
    19     Request request = targetRequest(template);
    20 
    21     if (logLevel != Logger.Level.NONE) {
    22         logger.logRequest(metadata.configKey(), logLevel, request);
    23     }
    24 
    25     Response response;
    26     long start = System.nanoTime();
    27     try {
    28         //通过 client 获得请求的返回值
    29         response = client.execute(request, options);
    30         // ensure the request is set. TODO: remove in Feign 10
    31         response.toBuilder().request(request).build();
    32     } catch (IOException e) {
    33         if (logLevel != Logger.Level.NONE) {
    34             logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
    35         }
    36         throw errorExecuting(request, e);
    37     }
    38     long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    39 
    40     boolean shouldClose = true;
    41     try {
    42         if (logLevel != Logger.Level.NONE) {
    43             response =
    44                 logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
    45             // ensure the request is set. TODO: remove in Feign 10
    46             //将request设置到response中
    47             response.toBuilder().request(request).build();
    48         }
    49         if (Response.class == metadata.returnType()) {
    50             if (response.body() == null) {
    51                 return response;
    52             }
    53             if (response.body().length() == null ||
    54                 response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
    55                 shouldClose = false;
    56                 return response;
    57             }
    58             // Ensure the response body is disconnected
    59             byte[] bodyData = Util.toByteArray(response.body().asInputStream());
    60             return response.toBuilder().body(bodyData).build();
    61         }
    62         if (response.status() >= 200 && response.status() < 300) {
    63             if (void.class == metadata.returnType()) {
    64                 return null;
    65             } else {
    66                 //解码
    67                 return decode(response);
    68             }
    69         } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
    70             return decode(response);
    71         } else {
    72             throw errorDecoder.decode(metadata.configKey(), response);
    73         }
    74     } 
    75     //...

    Client也有两种,一种直连,一种负载均衡,这里主要分析直连,负载均衡最后也要调用这里。

     Client.Default#execute

     调用HttpURLConnection进行http请求

      1 public Response execute(Request request, Options options) throws IOException {
      2     HttpURLConnection connection = convertAndSend(request, options);
      3     return convertResponse(connection).toBuilder().request(request).build();
      4 }
      5 
      6 HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
      7     final HttpURLConnection
      8         connection =
      9         (HttpURLConnection) new URL(request.url()).openConnection();
     10     if (connection instanceof HttpsURLConnection) {
     11         HttpsURLConnection sslCon = (HttpsURLConnection) connection;
     12         if (sslContextFactory != null) {
     13             sslCon.setSSLSocketFactory(sslContextFactory);
     14         }
     15         if (hostnameVerifier != null) {
     16             sslCon.setHostnameVerifier(hostnameVerifier);
     17         }
     18     }
     19     connection.setConnectTimeout(options.connectTimeoutMillis());
     20     connection.setReadTimeout(options.readTimeoutMillis());
     21     connection.setAllowUserInteraction(false);
     22     connection.setInstanceFollowRedirects(options.isFollowRedirects());
     23     connection.setRequestMethod(request.method());
     24 
     25     Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
     26     boolean
     27         gzipEncodedRequest =
     28         contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);
     29     boolean
     30         deflateEncodedRequest =
     31         contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);
     32 
     33     boolean hasAcceptHeader = false;
     34     Integer contentLength = null;
     35     for (String field : request.headers().keySet()) {
     36         if (field.equalsIgnoreCase("Accept")) {
     37             hasAcceptHeader = true;
     38         }
     39         for (String value : request.headers().get(field)) {
     40             if (field.equals(CONTENT_LENGTH)) {
     41                 if (!gzipEncodedRequest && !deflateEncodedRequest) {
     42                     contentLength = Integer.valueOf(value);
     43                     connection.addRequestProperty(field, value);
     44                 }
     45             } else {
     46                 connection.addRequestProperty(field, value);
     47             }
     48         }
     49     }
     50     // Some servers choke on the default accept string.
     51     if (!hasAcceptHeader) {
     52         connection.addRequestProperty("Accept", "*/*");
     53     }
     54 
     55     if (request.body() != null) {
     56         if (contentLength != null) {
     57             connection.setFixedLengthStreamingMode(contentLength);
     58         } else {
     59             connection.setChunkedStreamingMode(8196);
     60         }
     61         connection.setDoOutput(true);
     62         OutputStream out = connection.getOutputStream();
     63         if (gzipEncodedRequest) {
     64             out = new GZIPOutputStream(out);
     65         } else if (deflateEncodedRequest) {
     66             out = new DeflaterOutputStream(out);
     67         }
     68         try {
     69             out.write(request.body());
     70         } finally {
     71             try {
     72                 out.close();
     73             } catch (IOException suppressed) { // NOPMD
     74             }
     75         }
     76     }
     77     return connection;
     78 }
     79 
     80 Response convertResponse(HttpURLConnection connection) throws IOException {
     81     int status = connection.getResponseCode();
     82     String reason = connection.getResponseMessage();
     83 
     84     if (status < 0) {
     85         throw new IOException(format("Invalid status(%s) executing %s %s", status,
     86                                      connection.getRequestMethod(), connection.getURL()));
     87     }
     88 
     89     Map<String, Collection<String>> headers = new LinkedHashMap<String, Collection<String>>();
     90     for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {
     91         // response message
     92         if (field.getKey() != null) {
     93             headers.put(field.getKey(), field.getValue());
     94         }
     95     }
     96 
     97     Integer length = connection.getContentLength();
     98     if (length == -1) {
     99         length = null;
    100     }
    101     InputStream stream;
    102     if (status >= 400) {
    103         stream = connection.getErrorStream();
    104     } else {
    105         stream = connection.getInputStream();
    106     }
    107     return Response.builder()
    108         .status(status)
    109         .reason(reason)
    110         .headers(headers)
    111         .body(stream, length)
    112         .build();
    113 }

    最后放一张读源码时画的脑图。

    转载:https://blog.csdn.net/sinat_29899265/article/details/86577997 

    带着疑问去思考,然后串联,进而归纳总结,不断追问自己,进行自我辩证,像侦查嫌疑案件一样看待技术问题,漆黑的街道,你我一起寻找线索,你就是技术界大侦探福尔摩斯
  • 相关阅读:
    [BinaryTree] AVL树、红黑树、B/B+树和Trie树的比较
    [C/C++] 堆和栈的区别
    HBuilde H5开发,关于JSON的Storage存储
    你是怎么调试 JavaScript 程序
    plus.webview.create mui.openWindow区别是什么呢
    H5本地存储详细使用教程(localStorage + JSON数据存储应用框架)
    [工具教程] HBuilder调试夜神安卓模拟器方法(该方法真实有效)
    HBuilder使用夜神模拟器调试Android应用
    Android如何使用API
    Linux下安装方法总结(源码安装)
  • 原文地址:https://www.cnblogs.com/cainiao-Shun666/p/14620184.html
Copyright © 2020-2023  润新知