Fegin源码分析

Fegin源码分析

思考Feign要做的事情

  • 参数的解析和组装
  • 针对指定的FeginClient生成代理对象
  • 针对FeginClient中的方法描述进行解析
  • 组装成一个request对象,发起请求

Bean的动态装载

在了解Fegin的源码之前先了解一下Spring Boot中Bean的动态装载,有两种方式

  • ImportSelector 可以实现Bean的批量动态装载
  • ImportBeanDefinitionRegistrar 可以实现Bean的动态装载

ImportBeanDefinitionRegistrar

这是一个接口,通过BeanDefinitionRegistry注册Bean对象,接下来演示Bean的动态装载

HelloService

定义一个HelloService的Bean

@Data
public class HelloService{
    private String name;
}
GxImportBeanDefinitionRegistrar

自定义ImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口

public class GxImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition=new GenericBeanDefinition();
        beanDefinition.setBeanClassName(HelloService.class.getName());
        registry.registerBeanDefinition("helloService",beanDefinition);
    }
}
EnableGxRegistrara

添加EnableGxRegistrara注解,并且用@Import(GxImportBeanDefinitionRegistrar.class)导入刚刚定义的ImportBeanDefinitionRegistrar

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(GxImportBeanDefinitionRegistrar.class)
public @interface EnableGxRegistrara {

}
SpringCloudUserServiceApplication

在我们的启动类上添加 @EnableGxRegistrara 注解,然后就可以从spring容器中获取我们刚刚注册的HelloService实例了.

@EnableGxRegistrara
@EnableFeignClients(basePackages = "com.gxedu.example.clients")
@SpringBootApplication
public class SpringCloudUserServiceApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext contxt=SpringApplication.run(SpringCloudUserServiceApplication.class, args);
        System.out.println(contxt.getBean(HelloService.class));
    }

}

这就是动态装载的功能实现,它相比于@Configuration配置注入,会多了很多的灵活性。

源码解析

@EnableFeignClients(basePackages = “com.gxedu.example.clients”)

@EnableGxRegistrara
@EnableFeignClients(basePackages = "com.gxedu.example.clients")
@SpringBootApplication
public class SpringCloudUserServiceApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext contxt=SpringApplication.run(SpringCloudUserServiceApplication.class, args);
        System.out.println(contxt.getBean(HelloService.class));
    }
}

这个注解的声明如下,它用到了一个@Import注解,我们知道Import是用来导入一个配置类的,接下来 去看一下FeignClientsRegistrar的定义

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

FeignClientsRegistrar.registerBeanDefinitions()

FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,它是一个动态注入bean的接口, Spring Boot启动的时候,会去调用这个类中的registerBeanDefinitions来实现动态Bean的装载。 它的作用类似于ImportSelector。

class FeignClientsRegistrar
    implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {  
    
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//注册@EnableFeignClients中定义defaultConfiguration属性下的类,包装成FeignClientSpecification,注册到Spring容器。
//在@FeignClient中有一个属性:configuration,这个属性是表示各个FeignClient自定义的配置类,后面也会通过调用registerClientConfiguration方法来注册成FeignClientSpecification到容器。
//所以,这里可以完全理解在@EnableFeignClients中配置的是做为兜底的配置,在各个@FeignClient配置的就是自定义的情况。
        this.registerDefaultConfiguration(metadata, registry);
        this.registerFeignClients(metadata, registry);
    }
}
  • registerDefaultConfiguration 方法内部从 SpringBoot 启动类上检查是否有 @EnableFeignClients, 有该注解的话, 则完成 Feign 框架相关的一些配置内容注册
  • registerFeignClients 方法内部从 classpath 中, 扫描获得 @FeignClient 修饰的类, 将类的内容解析为 BeanDefinition , 最终通过调用 Spring 框架中的 BeanDefinitionReaderUtils.resgisterBeanDefinition 将解析处理过的 FeignClient BeanDeifinition 添加到 spring 容器中.

registerFeignClients(metadata, registry);

这个方法主要是扫描类路径下所有的 @FeignClient注解,然后进行动态Bean的注入。它最终会调用 registerFeignClient 方法。

public void registerFeignClients(AnnotationMetadata metadata,
                                 BeanDefinitionRegistry registry) {
    ...........
                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }
}

在这个方法中,就是去组装BeanDefinition,也就是Bean的定义,然后注册到Spring IOC容器。

private void registerFeignClient(BeanDefinitionRegistry registry,
                                 AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    //构建一个BeanDefinitionBuilder,参数传递FactoryBean
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
        .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = 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(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    String alias = contextId + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

    boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
    // null

    beanDefinition.setPrimary(primary);

    String qualifier = getQualifier(attributes);
    if (StringUtils.hasText(qualifier)) {
        alias = qualifier;
    }

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

我们关注一下,BeanDefinitionBuilder是用来构建一个BeanDefinition的,它是通过 genericBeanDefinition 来构建的,并且传入了一个FeignClientFactoryBean的类,FeignClient被动态注册成了一个FactoryBean。

Spring Cloud FengnClient实际上是利用Spring的代理工厂来生成代理类,所以在这里地方才会 把所有的FeignClient的BeanDefinition设置为FeignClientFactoryBean类型,而 FeignClientFactoryBean继承自FactoryBean,它是一个工厂Bean。

在Spring中,FactoryBean是一个工厂Bean,用来创建代理Bean。 工厂 Bean 是一种特殊的 Bean, 对于 Bean 的消费者来说, 他逻辑上是感知不到这个 Bean 是普 通的 Bean 还是工厂 Bean, 只是按照正常的获取 Bean 方式去调用, 但工厂bean 最后返回的实 例不是工厂Bean 本身, 而是执行工厂 Bean 的 getObject() 逻辑返回的示例。

BeanDefinitionBuilder.genericBeanDefinition()

public static BeanDefinitionBuilder genericBeanDefinition(Class<?> beanClass) {
    BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
    builder.beanDefinition.setBeanClass(beanClass);
    return builder;
}

简单来说,FeignClient标注的这个接口,会通过FeignClientFactoryBean.getObject()这个方法获得一 个代理对象。

FeignClientFactoryBean.getObject()

getObject调用的是getTarget()方法,它从applicationContext取出FeignContext,FeignContext继承了 NamedContextFactory,它是用来来统一维护feign中各个feign客户端相互隔离的上下文。

class FeignClientFactoryBean
    implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

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

    <T> T getTarget() {
        //实例化Feign上下文对象FeignContext
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
        //根据上下文对象构建Encoder,Decoder,LoadBalance,返回一个Builder对象
        Feign.Builder builder = feign(context);
		//如果url为空,则走负载均衡,生成有负载均衡功能的代理类
        if (!StringUtils.hasText(this.url)) {
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            }
            else {
                this.url = this.name;
            }
            this.url += cleanPath();
            //负载均衡的功能的代理类
            return (T) loadBalance(builder, context,
                                   new HardCodedTarget<>(this.type, this.name, this.url));
        }
        //如果指定了url,则生成默认的代理类
        if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
            this.url = "http://" + this.url;
        }
        String url = this.url + cleanPath();
        Client client = getOptional(context, Client.class);
        if (client != null) {
            if (client instanceof LoadBalancerFeignClient) {
                // not load balancing because we have a url,
                // but ribbon is on the classpath, so unwrap
                client = ((LoadBalancerFeignClient) client).getDelegate();
            }
            if (client instanceof FeignBlockingLoadBalancerClient) {
                // not load balancing because we have a url,
                // but Spring Cloud LoadBalancer is on the classpath, so unwrap
                client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
            }
            builder.client(client);
        }
        }//生成默认代理类
        Targeter targeter = get(context, Targeter.class);
        return (T) targeter.target(this, builder, context,
                                   new HardCodedTarget<>(this.type, this.name, url));
    }
}

FeignContext注册到容器是在FeignAutoConfiguration上完成

@ConditionalOnClass({Feign.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
@Import({DefaultGzipDecoderConfiguration.class})
public class FeignAutoConfiguration {
    
    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }
}

在初始化FeignContext时,会把configurations在容器中放入FeignContext中。configurations的来源就是在前面registerFeignClients方法中将@FeignClient的配置configuration。

接着,构建feign.builder,在构建时会向FeignContext获取配置的Encoder,Decoder等各种信息。 FeignContext在上面已经提到会为每个Feign客户端分配了一个容器,它们的父容器就是spring容器 配置完Feign.Builder之后,再判断是否需要LoadBalance,如果需要,则通过LoadBalance的方法来设 置。实际上他们最终调用的是Target.target()方法。

FeignContext

如何隔离bean,奥秘就在FeignContext.class这个类,首先看获取FeignClient的源码中有这样一段:

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
		ApplicationContextAware {
        
	/**
	 * @param <T> the target type of the Feign client
	 * @return a {@link Feign} client created with the specified data and the context information
	 */
	<T> T getTarget() {
		FeignContext context = applicationContext.getBean(FeignContext.class);
		Feign.Builder builder = feign(context);
}
feign(context);

我们可以看到在build的时候从容器中拿到了一个FeignContext的实例,再看生成builder的方法:

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

    // @formatter:off
    Feign.Builder builder = get(context, Feign.Builder.class)
        // required values
        .logger(logger)
        .encoder(get(context, Encoder.class))
        .decoder(get(context, Decoder.class))
        .contract(get(context, Contract.class));
    // @formatter:on

    configureFeign(context, builder);

    return builder;
}

大家可以跟进去get(context, Encoder.class),可以看到构造builder时,是从这个FeignContext中获取对应的ecoder实例的,那么FeignContext到底是什么呢?

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

public <T> T getInstance(String name, Class<T> type) {
    //调用getContext(),获取applicationContext
    AnnotationConfigApplicationContext context = this.getContext(name);
    return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0 ? context.getBean(type) : null;
}

protected AnnotationConfigApplicationContext getContext(String name) {
    //先从缓存中查找,如果有直接返回,
    if (!this.contexts.containsKey(name)) {
        Map var2 = this.contexts;
        synchronized(this.contexts) {
            if (!this.contexts.containsKey(name)) {
                //没有的话,重新构建一个,调用createContext()方法
                this.contexts.put(name, this.createContext(name));
            }
        }
    }

    return (AnnotationConfigApplicationContext)this.contexts.get(name);
}

FeignContext的源码中可以看到,它是继承自NamedContextFactory的一个类,这个类主要的两个属性如下:

private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

private ApplicationContext parent;

contexts是一个map,key是feign的name,value是一个AnnotationConfigApplicationContext,从protected AnnotationConfigApplicationContext createContext(String name)的源码中可以看出,每个context会去解析配置在FeignClient中的configuration类,将类中定义的@bean注册到当前的AnnotationConfigApplicationContext里,同时将容器的context设置为自己的父context:

protected AnnotationConfigApplicationContext createContext(String name) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    if (this.configurations.containsKey(name)) {
        for (Class<?> configuration : this.configurations.get(name)
             .getConfiguration()) {
            //注册配置类中的bean
            context.register(configuration);
        }
    }
    for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
        if (entry.getKey().startsWith("default.")) {
            for (Class<?> configuration : entry.getValue().getConfiguration()) {
                context.register(configuration);
            }
        }
    }
    context.register(PropertyPlaceholderAutoConfiguration.class,
                     this.defaultConfigType);
    context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
        this.propertySourceName,
        Collections.<String, Object> singletonMap(this.propertyName, name)));
    if (this.parent != null) {
        //设置自己的父context为容器的context
        context.setParent(this.parent);
    }
    context.setDisplayName(generateDisplayName(name));
    context.refresh();
    return context;
}

然后在生成FeignClient的时候,获取作用在该Client上的组件时,调用如下方法:

public <T> T getInstance(String name, Class<T> type) {
    //获取该Feign对应的context
    AnnotationConfigApplicationContext context = getContext(name);
    if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
                                                            type).length > 0) {
        //从自己的context中获取对应的组件,会依次往上从父context中寻找
        return context.getBean(type);
    }
    return null;
}

至此,就搞清了Feign是如何隔离开不同FeignClient的配置。

loadBalance

生成具备负载均衡能力的feign客户端,为feign客户端构建起绑定负载均衡客户端

Client client = (Client)this.getOptional(context, Client.class); 从上下文中获取一个 Client,默认是LoadBalancerFeignClient。

它是在FeignRibbonClientAutoConfiguration这个自动装配类中,通过Import实现的

@AutoConfigureBefore({FeignAutoConfiguration.class})
@EnableConfigurationProperties({FeignHttpClientProperties.class})
@Import({HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class})
public class FeignRibbonClientAutoConfiguration {
    
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
@Import(OkHttpFeignConfiguration.class)
class OkHttpFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
        OkHttpClient delegate = new OkHttpClient(okHttpClient);
        return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
    }

}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
                            HardCodedTarget<T> target) {
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        Targeter targeter = get(context, Targeter.class);
        return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
        "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

DefaultTarget.target

class DefaultTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
                        FeignContext context, Target.HardCodedTarget<T> target) {
        return feign.target(target);
    }

}

ReflectiveFeign.newInstance

public <T> T newInstance(Target<T> target) {
    //根据接口类和Contract协议解析方式,解析接口类上的方法和注解,转换成内部的MethodHandler处理方式
    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)));
            }
        }
    }
	// 基于Proxy.newProxyInstance 为接口类创建动态实现,将所有的请求转换给InvocationHandler 处理。
    InvocationHandler handler = this.factory.create(target, methodToHandler);
    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;
}

接口定义的参数解析

根据FeignClient接口的描述解析出对应的请求数据。

targetToHandlersByName.apply(target)

根据Contract协议规则,解析接口类的注解信息,解析成内部表现: targetToHandlersByName.apply(target);会解析接口方法上的注解,从而解析出方法粒度的特定的配 置信息,然后生产一个SynchronousMethodHandler 然后需要维护一个 的map,放入InvocationHandler的实现FeignInvocationHandler中。

public Map<String, MethodHandler> apply(Target target) {
    List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(target.type());
    Map<String, MethodHandler> result = new LinkedHashMap();
    Iterator var4 = metadata.iterator();

    while(var4.hasNext()) {
        MethodMetadata md = (MethodMetadata)var4.next();
        Object buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
            buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
        } else if (md.bodyIndex() != null) {
            buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
        } else {
            buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
        }

        if (md.isIgnored()) {
            result.put(md.configKey(), (args) -> {
                throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
            });
        } else {
            result.put(md.configKey(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
        }
    }

    return result;
}

this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder)

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

factory.create(target, methodToHandler);

static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
        return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
}

SpringMvcContract

当前Spring Cloud 微服务解决方案中,为了降低学习成本,采用了Spring MVC的部分注解来完成 请求 协议解析,也就是说 ,写客户端请求接口和像写服务端代码一样:客户端和服务端可以通过SDK的方式 进行约定,客户端只需要引入服务端发布的SDK API,就可以使用面向接口的编码方式对接服务。

OpenFeign调用过程

在前面的分析中,我们知道OpenFeign最终返回的是一个#ReflectiveFeign.FeignInvocationHandler的 对象。 那么当客户端发起请求时,会进入到FeignInvocationHandler.invoke方法中,这个大家都知道,它是 一个动态代理的实现。

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

而接着,在invoke方法中,会调用 this.dispatch.get(method)).invoke(args) 。 this.dispatch.get(method) 会返回一个SynchronousMethodHandler,进行拦截处理。 这个方法会根据参数生成完成的RequestTemplate对象,这个对象是Http请求的模版,代码如下。

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

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

动态生成Request

executeAndDecode

经过上述的代码,我们已经将restTemplate拼装完成,上面的代码中有一个 executeAndDecode() 方 法,该方法通过RequestTemplate生成Request请求对象,然后利用Http Client获取response,来获取 响应信息。

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    //转化为Http请求报文
    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);
        //获取返回结果
        response = response.toBuilder()
            .request(request)
            .requestTemplate(template)
            .build();
    } 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());
        }
    }
}

Client.execute

默认采用JDK的 HttpURLConnection 发起远程调用。

@Override
public Response execute(Request request, Options options) throws IOException {
    HttpURLConnection connection = convertAndSend(request, options);
    return convertResponse(connection, request);
}

Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
    int status = connection.getResponseCode();
    String reason = connection.getResponseMessage();

    if (status < 0) {
        throw new IOException(format("Invalid status(%s) executing %s %s", status,
                                     connection.getRequestMethod(), connection.getURL()));
    }

    Map<String, Collection<String>> headers = new LinkedHashMap<>();
    for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {
        // response message
        if (field.getKey() != null) {
            headers.put(field.getKey(), field.getValue());
        }
    }

    Integer length = connection.getContentLength();
    if (length == -1) {
        length = null;
    }
    InputStream stream;
    if (status >= 400) {
        stream = connection.getErrorStream();
    } else {
        stream = connection.getInputStream();
    }
    return Response.builder()
        .status(status)
        .reason(reason)
        .headers(headers)
        .request(request)
        .body(stream, length)
        .build();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值