Java中的HTTP客户端工具——RestTemplate

RestTemplate是由Spring框架提供的一个可用于应用中调用rest服务的类它简化了与http服务的通信方式。
统一了RESTFul的标准,封装了http连接,只需要传入url及其返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更为优雅的调用RESTFul服务的方式。RestTemplate类的设计原则与许多其他Spring的模板类(例如JdbcTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。

RestTemplate

RestTemplate类结构图

RestOperations:定义了RestTemplate对GET、POST、PUT、DELETE、OPTIONS方法的封装和适配,还有通用的exchange、execute方法。
HttpAccessor:定义了ClientHttpRequestFactory对象的get、set方法。
InterceptingHttpAccessor:定义了一个接口ClientHttpRequestInterceptor,用于做请求拦截。

RestTemplate源码
构造方法

    public RestTemplate() {
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
        this.messageConverters.add(new ResourceHttpMessageConverter(false));
        if (!shouldIgnoreXml) {
            try {
                this.messageConverters.add(new SourceHttpMessageConverter<>());
            }
            catch (Error err) {
                // Ignore when no TransformerFactory implementation is available
            }
        }
        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

        if (romePresent) {
            this.messageConverters.add(new AtomFeedHttpMessageConverter());
            this.messageConverters.add(new RssChannelHttpMessageConverter());
        }

        if (!shouldIgnoreXml) {
            if (jackson2XmlPresent) {
                this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
            }
            else if (jaxb2Present) {
                this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
            }
        }

        if (jackson2Present) {
            this.messageConverters.add(new MappingJackson2HttpMessageConverter());
        }
        else if (gsonPresent) {
            this.messageConverters.add(new GsonHttpMessageConverter());
        }
        else if (jsonbPresent) {
            this.messageConverters.add(new JsonbHttpMessageConverter());
        }
        else if (kotlinSerializationJsonPresent) {
            this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
        }

        if (jackson2SmilePresent) {
            this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
        }
        if (jackson2CborPresent) {
            this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
        }

        this.uriTemplateHandler = initUriTemplateHandler();
    }

    public RestTemplate(ClientHttpRequestFactory requestFactory) {
        this();
        setRequestFactory(requestFactory);
    }

    public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
        validateConverters(messageConverters);
        this.messageConverters.addAll(messageConverters);
        this.uriTemplateHandler = initUriTemplateHandler();
    }

RestTemplate的构造过程:
1.添加了多个HttpMessageConverter后续用于http请求响应的数据转换
2.初始化Uri模板的处理器,用于处理uri相关的东西
3.HttpAccessor默认创建了一个ClientHttpRequestFactory的成员实例(SimpleClientHttpRequestFactory,默认使用HttpURLConnection),用于创建请求对象。

RestTemplate类中常用的请求方法

官方支持:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
Get请求相关源码

@Override
    @Nullable
    public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    @Nullable
    public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

    @Override
    @Nullable
    public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
        return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
    }

    @Override
    public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
    }

    @Override
    public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
            throws RestClientException {

        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
    }

    @Override
    public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
        RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor));
    }

getForObject:返回值直接是响应体内容转为的 JSON 对象
getForEntity:返回值的封装包含有响应头, 响应状态码的 ResponseEntity对象

参数说明:

url:请求路径
requestEntity:HttpEntity对象,封装了请求头和请求体
responseType:返回数据类型
uriVariables:支持PathVariable类型的数据。

RestTemplate的核心组件

HttpMessageConverter:转换http请求响应过程中的消息数据

用于在 HTTP 请求和响应之间进行转换的策略接口。
需要对web接口的输入输出需要做自定义扩展或格式化时,都会考虑通过实现自定义的HttpMessageConverter来实现。

public interface HttpMessageConverter<T> {

    //指示此转换器是否可以读取给定的类。
    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

    //指示此转换器是否可以编写给定的类。
    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

    //返回此转换器支持的媒体类型列表。
    List<MediaType> getSupportedMediaTypes();

    //返回此转换器支持的给定类的媒体类型列表。
    default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
        return (canRead(clazz, null) || canWrite(clazz, null) ?
                getSupportedMediaTypes() : Collections.emptyList());
    }

    //从给定的输入消息中读取给定类型的对象,并返回它。
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException;

    //将给定对象写入给定的输出消息。
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException;

}
1.AbstractJackson2HttpMessageConverter:Jackson 转换器
AbstractJackson2HttpMessageConverter类结构图

具体实现类:
MappingJackson2CborHttpMessageConverter:读取和写入CBOR 数据格式。
MappingJackson2HttpMessageConverter:ObjectMapper读写 JSON。
MappingJackson2SmileHttpMessageConverter:读取和写入 Smile 数据格式(“二进制 JSON”)。
MappingJackson2XmlHttpMessageConverter:读取和写入 XML 编码数据。

2.AbstractJsonHttpMessageConverter:JSON 转换器
AbstractJsonHttpMessageConverter类结构图

具体实现类:
GsonHttpMessageConverter:使用Google Gson 库读写 JSON。
JsonbHttpMessageConverter:使用JSON Binding API 读写 JSON。

3.AbstractXmlHttpMessageConverter:XML 转换器

AbstractXmlHttpMessageConverter类结构图

具体实现类:
Jaxb2CollectionHttpMessageConverter:读取包含使用XmlRootElement和XmlType注释的类的集合。请注意,此转换器不支持写入。
Jaxb2RootElementHttpMessageConverter:读取使用XmlRootElement和XmlType注释的类,并编写使用XmlRootElement注释的类或其子类。
MarshallingHttpMessageConverter:读取使用XmlRootElement和XmlType注释的类,并编写使用XmlRootElement注释的类或其子类。

4.AbstractWireFeedHttpMessageConverter:Atom 和 RSS Feed 消息转换器

AbstractWireFeedHttpMessageConverter类结构图

具体实现类:
AtomFeedHttpMessageConverter:可以读写 Atom 提要。具体来说,这个转换器可以处理来自ROME 项目的Feed对象。
RssChannelHttpMessageConverter:可以读写 RSS 提要。具体来说,这个转换器可以处理来自ROME 项目的Channel对象。

ClientHttpRequestFactory:用于根据URI和HttpMethod创建出一个ClientHttpRequest来发送请求

源码很简单,一个工厂接口,定义了生成ClientHttpRequest的方法。

public interface ClientHttpRequestFactory {
    ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}

具体实现类:
SimpleClientHttpRequestFactory:使用标准 JDK 工具创建请求。(默认)
HttpComponentsClientHttpRequestFactory:使用HttpClient 创建请求。
OkHttp3ClientHttpRequestFactory:使用OkHttp 3.x 创建请求。
BufferingClientHttpRequestFactory:在内存中缓冲所有传出和传入流。使用此包装器允许多次读取响应正文。
InterceptingClientHttpRequestFactory:支持ClientHttpRequestInterceptors的包装器。

SimpleClientHttpRequestFactory为什么是默认?
HttpAccessor抽象类源码

public abstract class HttpAccessor {
    protected final Log logger = HttpLogging.forLogName(getClass());

    private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

    private final List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>();

    public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
        Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
        this.requestFactory = requestFactory;
    }

    public ClientHttpRequestFactory getRequestFactory() {
        return this.requestFactory;
    }

    public void setClientHttpRequestInitializers(
            List<ClientHttpRequestInitializer> clientHttpRequestInitializers) {

        if (this.clientHttpRequestInitializers != clientHttpRequestInitializers) {
            this.clientHttpRequestInitializers.clear();
            this.clientHttpRequestInitializers.addAll(clientHttpRequestInitializers);
            AnnotationAwareOrderComparator.sort(this.clientHttpRequestInitializers);
        }
    }

    public List<ClientHttpRequestInitializer> getClientHttpRequestInitializers() {
        return this.clientHttpRequestInitializers;
    }

    protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
        ClientHttpRequest request = getRequestFactory().createRequest(url, method);
        initialize(request);
        if (logger.isDebugEnabled()) {
            logger.debug("HTTP " + method.name() + " " + url);
        }
        return request;
    }

    private void initialize(ClientHttpRequest request) {
        this.clientHttpRequestInitializers.forEach(initializer -> initializer.initialize(request));
    }

}

UriTemplateHandler:组装uri的模板处理器

默认实现类是DefaultUriBuilderFactory

public class DefaultUriBuilderFactory implements UriBuilderFactory
public interface UriBuilderFactory extends UriTemplateHandler

RequestCallback:回调接口

@FunctionalInterface
public interface RequestCallback {
    /**
     * 通过打开的ClientHttpRequest由RestTemplate.execute调用。
     * 不需要关心关闭请求或处理错误:这都将由RestTemplate处理。
     */
    void doWithRequest(ClientHttpRequest request) throws IOException;
}

ResponseErrorHandler:用于确定特定响应是否有错误的策略接口。

public interface ResponseErrorHandler {

    /**
     * 指示给定的响应是否有任何错误。
     * 实现通常会检查响应的HttpStatus 。
     */
    boolean hasError(ClientHttpResponse response) throws IOException;

    /**
     * 处理给定响应中的错误。
     * 仅当hasError(ClientHttpResponse)返回true时才调用此方法。
     */
    void handleError(ClientHttpResponse response) throws IOException;

    /**
     * 替代handleError(ClientHttpResponse)提供对请求 URL 和 HTTP 方法的访问的额外信息
     */
    default void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
        handleError(response);
    }
}

ResponseExtractor:提取返回数据,但无需担心异常处理或关闭资源。

@FunctionalInterface
public interface ResponseExtractor<T> {
    /**
     * 从给定的ClientHttpResponse中提取数据并返回。
     */
    @Nullable
    T extractData(ClientHttpResponse response) throws IOException;
}
RestTemplate请求示例

GET请求例子

    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        //三种方式请求
        String url = "https://restapi.amap.com/v3/weather/weatherInfo?city=110101&key=3ff9482454cb60bcb73f65c8c48d4209](https://restapi.amap.com/v3/weather/weatherInfo?city=110101&key=3ff9482454cb60bcb73f65c8c48d4209)";
        String result = restTemplate.getForObject(url, String.class);
        System.out.println(result);
    }

POST请求例子

    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        //三种方式请求
        String url = "https://restapi.amap.com/v3/weather/weatherInfo?city=110101&key=3ff9482454cb60bcb73f65c8c48d4209](https://restapi.amap.com/v3/weather/weatherInfo?city=110101&key=3ff9482454cb60bcb73f65c8c48d4209)";
        Map<String,Object> params=new HashMap<>();
        ResponseEntity<String> result = restTemplate.postForEntity(url,params, String.class);
        System.out.println(result.getStatusCode().getReasonPhrase());
        System.out.println(result.getStatusCodeValue());
        System.out.println(result.getBody());
    }
RestTemplate相关博客推荐

源码分析:https://blog.csdn.net/zhawengan/article/details/121042628
所有请求用法分析:https://blog.csdn.net/weixin_38987366/article/details/109701339
上传文件:https://blog.csdn.net/weixin_39614177/article/details/113542058
下载文件:https://zzzgd.blog.csdn.net/article/details/88915818
相关博客专栏:https://blog.csdn.net/hanxiaotongtong/category_11999208.html

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

推荐阅读更多精彩内容