RestTemplate

  最近使用restTemplate的时候遇到了很多问题,在排查问题的时候了解了下其中的源码。(这块没有文档,真的难受。)

spring boot 提供了RestTemplate自动配置类 RestTemplateAutoConfiguration。

@Configuration
@AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
@ConditionalOnClass(RestTemplate.class)
public class RestTemplateAutoConfiguration {

private final ObjectProvider<HttpMessageConverters> messageConverters;

   private final ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers;

   public RestTemplateAutoConfiguration(
           ObjectProvider<HttpMessageConverters> messageConverters,
           ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers) {
       this.messageConverters = messageConverters;
       this.restTemplateCustomizers = restTemplateCustomizers;
   }
……
} 

这里看到这个类是在消息转换器自动配置之后配置的,我们观察到spring 注入属性的时候用到了ObjectProvider这个类,这个类是spring 4.3出来的新特性,spring 4之后推荐构造函数注入,并且在4.3之后可以使用构造函数隐式注入,即如果没有在构造函数上加@Autowired,spring会从使用改类中默认的构造器注入,当然如果多个构造器就必须加上此注解了。ObjectProvider(spring里面为BeanObjectProvider)这个类解决了bean为空,和bean有多个实例的问题,里面包含getObject、getIfAvailable、getIfUnique等方法。

    @Bean
    @ConditionalOnMissingBean
    public RestTemplateBuilder restTemplateBuilder() {
        RestTemplateBuilder builder = new RestTemplateBuilder();
        HttpMessageConverters converters = this.messageConverters.getIfUnique();
        if (converters != null) {
            builder = builder.messageConverters(converters.getConverters());
        }

        List<RestTemplateCustomizer> customizers = this.restTemplateCustomizers
                .orderedStream().collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(customizers)) {
            builder = builder.customizers(customizers);
        }
        return builder;
    }

这个方法会构造一个RestTemplateBuilder交给spring管理,中间通过getIfUnique()获取到单例的HttpMessageConverters 和restTemplateCustomizers,把其中所有自动配置的消息转换器和所有的RestTemplateCustomizer(用于自定义restTemplate的回调接口)加入这个RestTemplateBuilder,然后返回一个完整的实例。当然方法首先new了一个RestTemplateBuilder,看看它的构造器给我们干了什么。

    public RestTemplateBuilder(RestTemplateCustomizer... customizers) {
        Assert.notNull(customizers, "Customizers must not be null");
        this.detectRequestFactory = true;
        this.rootUri = null;
        this.messageConverters = null;
        this.requestFactorySupplier = null;
        this.uriTemplateHandler = null;
        this.errorHandler = null;
        this.basicAuthentication = null;
        this.restTemplateCustomizers = Collections
                .unmodifiableSet(new LinkedHashSet<>(Arrays.asList(customizers)));
        this.requestFactoryCustomizer = new RequestFactoryCustomizer();
        this.interceptors = Collections.emptySet();
    }

这里给detectRequestFactory赋值成了true,目的是为了后面自动检测ClientHttpRequestFactory。

接下来我们使用的时候通常会将RestTemplateBuilder注入到要使用到bean里面,使用之前spring自动配置给我们带来的功能,并且我们可以附加很多功能,比如自定义 的HttpMessageConverter、自定义的RestTemplate、自定义的ClientHttpRequestFactory、自定义的异常处理和请求拦截器。

最终的目的是为了得到一个供我们使用的RestTemplate。我们通常会使用build()方法构建。 接下来看看build方法做了什么。

    public RestTemplate build() {
        return build(RestTemplate.class);
    }

    /**
     * Build a new {@link RestTemplate} instance of the specified type and configure it
     * using this builder.
     * @param <T> the type of rest template
     * @param restTemplateClass the template type to create
     * @return a configured {@link RestTemplate} instance.
     * @see RestTemplateBuilder#build()
     * @see #configure(RestTemplate)
     */

    public <T extends RestTemplate> T build(Class<T> restTemplateClass) {
        return configure(BeanUtils.instantiateClass(restTemplateClass));
    }

在我们没有自定义RestTemplate我们会调用上面方法,他会调用下面方法,帮我们创建一个RestTemplate的实例作为configure的参数,再来看configure方法:

public <T extends RestTemplate> T configure(T restTemplate) {
        configureRequestFactory(restTemplate);
        if (!CollectionUtils.isEmpty(this.messageConverters)) {
            restTemplate.setMessageConverters(new ArrayList<>(this.messageConverters));
        }
        if (this.uriTemplateHandler != null) {
            restTemplate.setUriTemplateHandler(this.uriTemplateHandler);
        }
        if (this.errorHandler != null) {
            restTemplate.setErrorHandler(this.errorHandler);
        }
        if (this.rootUri != null) {
            RootUriTemplateHandler.addTo(restTemplate, this.rootUri);
        }
        if (this.basicAuthentication != null) {
            restTemplate.getInterceptors().add(this.basicAuthentication);
        }
        restTemplate.getInterceptors().addAll(this.interceptors);
        if (!CollectionUtils.isEmpty(this.restTemplateCustomizers)) {
            for (RestTemplateCustomizer customizer : this.restTemplateCustomizers) {
                customizer.customize(restTemplate);
            }
        }
        return restTemplate;
    }

这里就是把刚刚自动配置的消息转换器,错误处理器,配置一些URI模板,URI路径的模板,发送请求的basic认证拦截器和其他一些拦截器(ClientHttpRequestInterceptor),一些自定义的restTemplate的会调接口。

然后继续看其中的

configureRequestFactory(restTemplate);
    private void configureRequestFactory(RestTemplate restTemplate) {
        ClientHttpRequestFactory requestFactory = null;
        if (this.requestFactorySupplier != null) {
            requestFactory = this.requestFactorySupplier.get();
        }
        else if (this.detectRequestFactory) {
            requestFactory = new ClientHttpRequestFactorySupplier().get();
        }
        if (requestFactory != null) {
            if (this.requestFactoryCustomizer != null) {
                this.requestFactoryCustomizer.accept(requestFactory);
            }
            restTemplate.setRequestFactory(requestFactory);
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,188评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,464评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,562评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,893评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,917评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,708评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,430评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,342评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,801评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,976评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,115评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,804评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,458评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,008评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,135评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,365评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,055评论 2 355

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,664评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,823评论 6 342
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,717评论 2 22
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong阅读 22,410评论 1 92
  • 1、不脱嫌热,脱后嫌冷,此乃春天。 2、不送不安,送后不廉,此乃春节。 3、不看失落,看后失望,此乃春晚。 4、不...
    Stephen潇雨阅读 244评论 0 0