一文吃透接口调用神器RestTemplate

文末可以领取所有系列高清 pdf。

大家好,我是路人,这是 SpringMVC 系列第 21 篇。

本文介绍 Spring web 中特别牛逼的一个类 RestTemplate。

目录

  • 1、RestTemplate 概述

  • 2、案例代码

    • 2.1、git 地址

    • 2.2、关键代码位置

    • 2.3、如何运行测试用例?

  • 3、发送 Get 请求

    • 3.1、普通请求

    • 3.2、url 中含有动态参数

    • 3.3、接口返回值为泛型

    • 3.4、下载小文件

    • 3.5、下载大文件

    • 3.6、传递头

    • 3.7、综合案例:含头、url 动态参数

  • 4、POST 请求

    • 4.1、post 请求常见的 3 种类型

    • 4.2、普通表单请求

    • 4.3、上传本地文件

    • 4.4、通过流或字节数组的方式上传文件

    • 4.5、复杂表单:多个普通元素+多文件上传

    • 4.6、发送 json 格式数据:传递 java 对象

    • 4.7、发送 json 格式数据:传递 java 对象,返回值为泛型

    • 4.8、发送 json 字符串格式数据

  • 5、DELETE、PUT、OPTION 请求

    • 5.1、DELETE 请求

    • 5.2、PUT 请求

    • 5.3、OPTIONS 请求

  • 6、集成 HttpClient

  • 7、集成 okhttp

  • 8、总结

  • 9、SpringMVC 系列目录

  • 10、更多好文章

  • 11、【路人甲 Java】所有系列高清 PDF

1、RestTemplate 概述

发送 http 请求,估计很多人用过 httpclient 和 okhttp,确实挺好用的,而 Spring web 中的 RestTemplate 和这俩的功能类似,也是用来发送 http 请求的,不过用法上面比前面的 2 位要容易很多。

spring 框架提供的 RestTemplate 类可用于在应用中调用 rest 服务,它简化了与 http 服务的通信方式,统一了 RESTful 的标准,封装了 http 链接, 我们只需要传入 url 及返回值类型即可。相较于之前常用的 HttpClient,RestTemplate 是一种更优雅的调用 RESTful 服务的方式。

在 Spring 应用程序中访问第三方 REST 服务与使用 Spring RestTemplate 类有关。RestTemplate 类的设计原则与许多其他 Spring 模板类(例如 JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。

RestTemplate 默认依赖 JDK 提供 http 连接的能力(HttpURLConnection),如果有需要的话也可以通过 setRequestFactory 方法替换为例如 Apache HttpComponents、Netty 或 OkHttp 等其它 HTTP library。

考虑到 RestTemplate 类是为调用 REST 服务而设计的,因此它的主要方法与 REST 的基础紧密相连就不足为奇了,后者是 HTTP 协议的方法:HEAD、GET、POST、PUT、DELETE 和 OPTIONS。例如,RestTemplate 类具有 headForHeaders()、getForObject()、postForObject()、put()和 delete()等方法。

下面给大家上案例,案例是重点,通过案例,把我知道的用法都给盘出来。

2、案例代码

2.1、git 地址

https://gitee.com/javacode2018/springmvc-series

10d8584b55c22fe2e509a0a93fc0126a.png

2.2、关键代码位置

文中的所有 controller 代码,在RestTemplateTestController类中。

所有@Test 用例的代码,在RestTemplateTest

3c0bd3eaa63e802b41c52ec909fae3a5.png

2.3、如何运行测试用例?

  • 拉取项目

  • 将 chat16-RestTemplate 模块发布到 tomcat9 中

  • 运行 RestTemplateTest 中对应的用例即可

下面咱们来看 RestTemplate 常见的用法汇总。

3、发送 Get 请求

3.1、普通请求

接口代码

@GetMapping("/test/get")
@ResponseBody
public BookDto get() {
    return new BookDto(1, "SpringMVC系列");
}

使用 RestTemplate 调用上面这个接口,通常有 2 种写法,如下

@Test
public void test1() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/get";
    //getForObject方法,获取响应体,将其转换为第二个参数指定的类型
    BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
    System.out.println(bookDto);
}

@Test
public void test2() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/get";
    //getForEntity方法,返回值为ResponseEntity类型
    // ResponseEntity中包含了响应结果中的所有信息,比如头、状态、body
    ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, BookDto.class);
    //状态码
    System.out.println(responseEntity.getStatusCode());
    //获取头
    System.out.println("头:" + responseEntity.getHeaders());
    //获取body
    BookDto bookDto = responseEntity.getBody();
    System.out.println(bookDto);
}

test1 输出

BookDto{id=1, name='SpringMVC系列'}

test2 输出

200 OK
头:[Content-Type:"application/json;charset=UTF-8", Transfer-Encoding:"chunked", Date:"Sat, 02 Oct 2021 07:05:15 GMT", Keep-Alive:"timeout=20", Connection:"keep-alive"]
BookDto{id=1, name='SpringMVC系列'}

3.2、url 中含有动态参数

接口代码

@GetMapping("/test/get/{id}/{name}")
@ResponseBody
public BookDto get(@PathVariable("id") Integer id, @PathVariable("name") String name) {
    return new BookDto(id, name);
}

使用 RestTemplate 调用上面这个接口,通常有 2 种写法,如下

@Test
public void test3() {
    RestTemplate restTemplate = new RestTemplate();
    //url中有动态参数
    String url = "http://localhost:8080/chat16/test/get/{id}/{name}";
    Map<String, String> uriVariables = new HashMap<>();
    uriVariables.put("id", "1");
    uriVariables.put("name", "SpringMVC系列");
    //使用getForObject或者getForEntity方法
    BookDto bookDto = restTemplate.getForObject(url, BookDto.class, uriVariables);
    System.out.println(bookDto);
}

@Test
public void test4() {
    RestTemplate restTemplate = new RestTemplate();
    //url中有动态参数
    String url = "http://localhost:8080/chat16/test/get/{id}/{name}";
    Map<String, "SpringMVC系列");
    //getForEntity方法
    ResponseEntity<BookDto> responseEntity = restTemplate.getForEntity(url, uriVariables);
    BookDto bookDto = responseEntity.getBody();
    System.out.println(bookDto);
}

test3 输出

BookDto{id=1, name='SpringMVC系列'}

test4 输出

BookDto{id=1, name='SpringMVC系列'}

3.3、接口返回值为泛型

接口代码

@GetMapping("/test/getList")
@ResponseBody
public List<BookDto> getList() {
    return Arrays.asList(
            new BookDto(1, "Spring高手系列"),            new BookDto(2, "SpringMVC系列")
    );
}

当接口的返回值为泛型的时候,这种情况比较特殊,使用 RestTemplate 调用上面这个接口,代码如下,需要用到restTemplate.exchange的方法,这个方法中有个参数是ParameterizedTypeReference类型,通过这个参数类指定泛型类型

@Test
public void test5() {
    RestTemplate restTemplate = new RestTemplate();
    //返回值为泛型
    String url = "http://localhost:8080/chat16/test/getList";
    //若返回结果是泛型类型的,需要使用到exchange方法,
    //这个方法中有个参数是ParameterizedTypeReference类型,通过这个参数类指定泛型类型
    ResponseEntity<List<BookDto>> responseEntity =
            restTemplate.exchange(url,                    HttpMethod.GET,                    null,                    new ParameterizedTypeReference<List<BookDto>>() {
                    });
    List<BookDto> bookDtoList = responseEntity.getBody();
    System.out.println(bookDtoList);
}

输出

[BookDto{id=1, name='Spring高手系列'}, BookDto{id=2, name='SpringMVC系列'}]

3.4、下载小文件

接口代码如下,这个接口会下载服务器端的 1.txt 文件。

/**
 * 下载文件
 *
 * @return
 */
@GetMapping("/test/downFile")
@ResponseBody
public HttpEntity<InputStreamResource> downFile() {
    //将文件流封装为InputStreamResource对象
    InputStream inputStream = this.getClass().getResourceAsStream("/1.txt");
    InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
    //设置header
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=1.txt");
    HttpEntity<InputStreamResource> httpEntity = new HttpEntity<>(inputStreamResource);
    return httpEntity;
}

使用 RestTemplate 调用这个接口,代码如下,目前这个文件的内容比较少,可以直接得到一个数组。

@Test
public void test6() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/downFile";
    //文件比较小的情况,直接返回字节数组
    ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(url, byte[].class);
    //获取文件的内容
    byte[] body = responseEntity.getBody();
    String content = new String(body);
    System.out.println(content);
}

注意:如果文件大的时候,这种方式就有问题了,会导致 oom,要用下面的方式了。

3.5、下载大文件

接口代码,继续使用上面下载 1.txt 的代码

/**
 * 下载文件
 *
 * @return
 */
@GetMapping("/test/downFile")
@ResponseBody
public HttpEntity<InputStreamResource> downFile() {
    //将文件流封装为InputStreamResource对象
    InputStream inputStream = this.getClass().getResourceAsStream("/1.txt");
    InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
    //设置header
    MultiValueMap<String, "attachment;filename=1.txt");
    HttpEntity<InputStreamResource> httpEntity = new HttpEntity<>(inputStreamResource);
    return httpEntity;
}

此时使用 RestTemplate 调用这个接口,代码如下

文件比较大的时候,比如好几个 G,就不能返回字节数组了,会把内存撑爆,导致 OOM,需要使用 execute 方法了,这个方法中有个 ResponseExtractor 类型的参数,restTemplate 拿到结果之后,会回调{@link ResponseExtractor#extractData}这个方法,在这个方法中可以拿到响应流,然后进行处理,这个过程就是变读边处理,不会导致内存溢出

@Test
public void test7() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/downFile";
    /**
     * 文件比较大的时候,比如好几个G,就不能返回字节数组了,会把内存撑爆,导致OOM
     * 需要这么玩:
     * 需要使用execute方法了,这个方法中有个ResponseExtractor类型的参数,
     * restTemplate拿到结果之后,会回调{@link ResponseExtractor#extractData}这个方法,
     * 在这个方法中可以拿到响应流,然后进行处理,这个过程就是变读边处理,不会导致内存溢出
     */
    String result = restTemplate.execute(url,            HttpMethod.GET,            null,            new ResponseExtractor<String>() {
                @Override
                public String extractData(ClientHttpResponse response) throws IOException {
                    System.out.println("状态:"+response.getStatusCode());
                    System.out.println("头:"+response.getHeaders());
                    //获取响应体流
                    InputStream body = response.getBody();
                    //处理响应体流
                    String content = IOUtils.toString(body, "UTF-8");
                    return content;
                }
            }, new HashMap<>());

    System.out.println(result);
}

3.6、传递头

接口代码

@GetMapping("/test/header")
@ResponseBody
public Map<String, List<String>> header(HttpServletRequest request) {
    Map<String, List<String>> header = new LinkedHashMap<>();
    Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
        String name = headerNames.nextElement();
        Enumeration<String> values = request.getHeaders(name);
        List<String> list = new ArrayList<>();
        while (values.hasMoreElements()) {
            list.add(values.nextElement());
        }
        header.put(name, list);
    }
    return header;
}

使用 RestTemplate 调用接口,请求头中传递数据,代码如下,注意代码①和②,这两处是关键,用到了HttpHeadersRequestEntity

  • 请求头放在 HttpHeaders 对象中

  • RequestEntity:请求实体,请求的所有信息都可以放在 RequestEntity 中,比如 body 部分、头、请求方式、url 等信息

@Test
public void test8() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/header";
    //①:请求头放在HttpHeaders对象中
    MultiValueMap<String, String> headers = new HttpHeaders();
    headers.add("header-1", "V1");
    headers.add("header-2", "Spring");
    headers.add("header-2", "SpringBoot");
    //②:RequestEntity:请求实体,请求的所有信息都可以放在RequestEntity中,比如body部分、头、请求方式、url等信息
    RequestEntity requestEntity = new RequestEntity(
            null, //body部分数据
            headers, //头
            HttpMethod.GET,//请求方法
            URI.create(url) //地址
    );
    ResponseEntity<Map<String, List<String>>> responseEntity = restTemplate.exchange(requestEntity,            new ParameterizedTypeReference<Map<String, List<String>>>() {
            });
    Map<String, List<String>> result = responseEntity.getBody();
    System.out.println(result);
}

输出

{accept=[application/json, application/*+json], header-1=[V1], header-2=[Spring, SpringBoot], user-agent=[Java/1.8.0_121], host=[localhost:8080], connection=[keep-alive]}

3.7、综合案例:含头、url 动态参数

接口

@GetMapping("/test/getAll/{path1}/{path2}")
@ResponseBody
public Map<String, Object> getAll(@PathVariable("path1") String path1,                                  @PathVariable("path2") String path2,                                  HttpServletRequest request) {
    Map<String, Object> result = new LinkedHashMap<>();
    result.put("path1", path1);
    result.put("path2", path2);
    //头
    Map<String, list);
    }
    result.put("header", header);
    return result;
}

如下,使用 RestTemplate 调用接口,GET 方式、传递 header、path 中动态参数。

@Test
public void test9() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/getAll/{path1}/{path2}";
    //①:请求头
    MultiValueMap<String, "SpringBoot");
    //②:url中的2个参数
    Map<String, String> uriVariables = new HashMap<>();
    uriVariables.put("path1", "v1");
    uriVariables.put("path2", "v2");
    //③:HttpEntity:HTTP实体,内部包含了请求头和请求体
    HttpEntity requestEntity = new HttpEntity(
        null,//body部分,get请求没有body,所以为null
        headers //头
    );
    //④:使用exchange发送请求
    ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange(
        url, //url
        HttpMethod.GET, //请求方式
        requestEntity, //请求实体(头、body)
        new ParameterizedTypeReference<Map<String, Object>>() {
        },//返回的结果类型
        uriVariables //url中的占位符对应的值
    );
    Map<String, Object> result = responseEntity.getBody();
    System.out.println(result);
}

输出

{path1=v1, path2=v2, header={accept=[application/json, connection=[keep-alive]}}

4、POST 请求

4.1、post 请求常见的 3 种类型

http 请求头中的 Content-Type 用来指定请求的类型,常见的有 3 种

Content-Type 说明
application/x-www-form-urlencoded 页面中普通的 form 表单提交时就是这种类型,表单中的元素会按照名称和值拼接好,然后之间用&连接,格式如:p1=v1&p2=v2&p3=v3
然后通过 urlencoded 编码之后丢在 body 中发送
multipart/form-data 页面中表单上传文件的时候,用到的就是这种格式
application/json 将发送的数据转换为 json 格式,丢在 http 请求的 body 中发送,后端接口通常用@RequestBody 配合对象来接收。

下面看则种方式的案例。

4.2、普通表单请求

普通表单默认为 application/x-www-form-urlencoded 类型的请求。

接口代码

@PostMapping("/test/form1")
@ResponseBody
public BookDto form1(BookDto bookDto) {
    return bookDto;
}

使用 RestTemplate 调用接口

@Test
public void test10() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form1";
    //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String,List<String>>
    MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
    //调用add方法填充表单数据(表单名称:值)
    body.add("id","1");
    body.add("name","SpringMVC系列");
    //②:发送请求(url,请求体,返回值需要转换的类型)
    BookDto result = restTemplate.postForObject(url, body, BookDto.class);
    System.out.println(result);
}

如果想携带头信息,代码如下

@Test
public void test11() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form1";
    //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String, String> body = new LinkedMultiValueMap<>();
    //调用add方法放入表单元素(表单名称:值)
    body.add("id","SpringMVC系列");
    //②:请求头
    HttpHeaders headers = new HttpHeaders();
    //调用set方法放入请求头
    headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
    //③:请求实体:包含了请求体和请求头
    HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, headers);
    //④:发送请求(url,请求实体,返回值需要转换的类型)
    BookDto result = restTemplate.postForObject(url, httpEntity, BookDto.class);
    System.out.println(result);
}

4.3、上传本地文件

上传文件 Content-Type 为 multipart/form-data 类型。

接口如下,上传上传单个文件,返回值为一个 Map 类型,是泛型类型

@PostMapping(value = "/test/form2")
@ResponseBody
public Map<String, String> form2(@RequestParam("file1") MultipartFile file1) {
    Map<String, String> fileMetadata = new LinkedHashMap<>();
    fileMetadata.put("文件名", file1.getOriginalFilename());
    fileMetadata.put("文件类型", file1.getContentType());
    fileMetadata.put("文件大小(byte)", String.valueOf(file1.getSize()));
    return fileMetadata;
}

使用 RestTemplate 调用接口,主要下面代码上传的文件需要包装为org.springframework.core.io.Resource,常用的有 3 中[FileSystemResource、InputStreamResource、ByteArrayResource],这里案例中我们用到的是 FileSystemResource 来上传本地文件,另外 2 种(InputStreamResource、ByteArrayResource)用法就比较特殊了,见下个案例。

@Test
public void test12() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form2";
    //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String, Object> body = new LinkedMultiValueMap<>();
    //调用add方法放入表单元素(表单名称:值)
    //②:文件对应的类型,需要是org.springframework.core.io.Resource类型的,常见的有[FileSystemResource、InputStreamResource、ByteArrayResource]
    body.add("file1", new FileSystemResource(".\\src\\main\\java\\com\\javacode2018\\springmvc\\chat16\\dto\\UserDto.java"));
    //③:头
    HttpHeaders headers = new HttpHeaders();
    headers.add("header1", "v1");
    headers.add("header2", "v2");
    //④:请求实体
    RequestEntity<MultiValueMap<String, Object>> requestEntity = new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(url));
    //⑤:发送请求(请求实体,返回值需要转换的类型)
    ResponseEntity<Map<String, String>> responseEntity = restTemplate.exchange(
        requestEntity,        new ParameterizedTypeReference<Map<String, String>>() {
        });
    Map<String, String> result = responseEntity.getBody();
    System.out.println(result);
}

4.4、通过流或字节数组的方式上传文件

有时候,上传的文件是通过流的方式或者字节数组的方式,那么就需要用到 InputStreamResource、ByteArrayResource 这俩了。

**注意:**使用这俩的时候,需要重写 2 个方法,否则会上传失败

  • getFilename:文件名称

  • contentLength:长度

@Test
public void test13() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form2";
    //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String, Object> body = new LinkedMultiValueMap<>();
    /**
     * ②:通过流的方式上传文件,流的方式需要用到InputStreamResource类,需要重写2个方法
     * getFilename:文件名称
     * contentLength:长度
     */
    InputStream inputStream = RestTemplateTest.class.getResourceAsStream("/1.txt");
    InputStreamResource inputStreamResource = new InputStreamResource(inputStream) {
        @Override
        public String getFilename() {
            return "1.txt";
        }

        @Override
        public long contentLength() throws IOException {
            return inputStream.available();
        }
    };
    body.add("file1", inputStreamResource);
    //③:头
    HttpHeaders headers = new HttpHeaders();
    headers.add("header1", String>> responseEntity = restTemplate.exchange(
            requestEntity, String>>() {
            });
    Map<String, String> result = responseEntity.getBody();
    System.out.println(result);
}

4.5、复杂表单:多个普通元素+多文件上传

接口

/**
 * 复杂的表单:包含了普通元素、多文件
 *
 * @param userDto
 * @return
 */
@PostMapping("/test/form3")
@ResponseBody
public Map<String, String> form3(UserDto userDto) {
    Map<String, String> result = new LinkedHashMap<>();
    result.put("name", userDto.getName());
    result.put("headImg", userDto.getHeadImg().getOriginalFilename());
    result.put("idImgList", Arrays.toString(userDto.getIdImgList().stream().
                                            map(MultipartFile::getOriginalFilename).toArray()));
    return result;
}

UserDto:包含了多个元素(姓名、头像、多张证件照),这种可以模拟复杂的表单

public class UserDto {
    //姓名
    private String name;
    //头像
    private MultipartFile headImg;
    //多张证件照
    private List<MultipartFile> idImgList;

    //get set 省略了...
}

用 RestTemplate 调用这个接口,代码如下

@Test
public void test14() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form3";
    //①:表单信息,需要放在MultiValueMap中,MultiValueMap相当于Map<String, Object> body = new LinkedMultiValueMap<>();
    body.add("name", "路人");
    body.add("headImg", new FileSystemResource(".\\src\\main\\resources\\1.jpg"));
    //来2张证件照,元素名称一样
    body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\2.jpg"));
    body.add("idImgList", new FileSystemResource(".\\src\\main\\resources\\3.jpg"));
    //③:头
    HttpHeaders headers = new HttpHeaders();
    headers.add("header1", String> result = responseEntity.getBody();
    System.out.println(result);
}

输出

{name=路人, headImg=1.jpg, idImgList=[2.jpg, 3.jpg]}

4.6、发送 json 格式数据:传递 java 对象

接口

/**
 * body中json格式的数据,返回值非泛型
 *
 * @param bookDto
 * @return
 */
@PostMapping("/test/form4")
@ResponseBody
public BookDto form4(@RequestBody BookDto bookDto) {
    return bookDto;
}

RestTemplate 调用接口

@Test
public void test15() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form4";
    BookDto body = new BookDto(1, "SpringMVC系列");
    BookDto result = restTemplate.postForObject(url, BookDto.class);
    System.out.println(result);
}

输出

BookDto{id=1, name='SpringMVC系列'}

4.7、发送 json 格式数据:传递 java 对象,返回值为泛型

接口

/**
 * body中json格式的数据,返回值为泛型
 *
 * @param bookDtoList
 * @return
 */
@PostMapping("/test/form5")
@ResponseBody
public List<BookDto> form5(@RequestBody List<BookDto> bookDtoList) {
    return bookDtoList;
}

用 RestTemplate 调用这个接口,代码如下

@Test
public void test16() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form5";
    //①:请求体,发送的时候会被转换为json格式数据
    List<BookDto> body = Arrays.asList(
            new BookDto(1, "SpringMVC系列"), "MySQL系列"));
    //②:头
    HttpHeaders headers = new HttpHeaders();
    headers.add("header1", "v2");
    //③:请求实体
    RequestEntity requestEntity = new RequestEntity(body, URI.create(url));
    //④:发送请求(请求实体,返回值需要转换的类型)
    ResponseEntity<List<BookDto>> responseEntity = restTemplate.exchange(
            requestEntity,            new ParameterizedTypeReference<List<BookDto>>() {
            });
    //⑤:获取结果
    List<BookDto> result = responseEntity.getBody();
    System.out.println(result);
}

输出

[BookDto{id=1, name='SpringMVC系列'}, name='MySQL系列'}]

4.8、发送 json 字符串格式数据

上面 2 个 json 案例 body 都是 java 对象,RestTemplate 默认自动配上 Content-Type=application/json

但是如果 body 的值是 json 格式字符串的时候,调用的时候需要在头中明确指定 Content-Type=application/json,写法如下:

@Test
public void test17() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://localhost:8080/chat16/test/form5";
    //①:请求体为一个json格式的字符串
    String body = "[{\"id\":1,\"name\":\"SpringMVC系列\"},{\"id\":2,\"name\":\"MySQL系列\"}]";
    /**
     * ②:若请求体为json字符串的时候,需要在头中设置Content-Type=application/json;
     * 若body是普通的java类的时候,无需指定这个,RestTemplate默认自动配上Content-Type=application/json
     */
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    //③:请求实体(body,头、请求方式,uri)
    RequestEntity requestEntity = new RequestEntity(body, name='MySQL系列'}]

5、DELETE、PUT、OPTION 请求

5.1、DELETE 请求

public void delete(String url, Object... uriVariables);
public void delete(String url, Map<String, ?> uriVariables);
public void delete(URI url);

5.2、PUT 请求

PUT 请求和 POST 请求类似,将类型改为 PUT 就可以了。

5.3、OPTIONS 请求

OPTIONS 请求用来探测接口支持哪些 http 方法

public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables);
public Set<HttpMethod> optionsForAllow(String url, ?> uriVariables);
public Set<HttpMethod> optionsForAllow(URI url);

6、集成 HttpClient

RestTemplate 内部默认用的是 jdk 自带的 HttpURLConnection 发送请求的,性能上面并不是太突出。

可以将其替换为 httpclient 或者 okhttp。

先来看下如何替换为 HttpClient。

引入 maven 配置

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.7</version>
</dependency>

创建 RestTemplate 时指定 HttpClient 配置,代码如下

public HttpClient httpClient() {
    HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
    try {
        //设置信任ssl访问
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();
        httpClientBuilder.setSSLContext(sslContext);
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                // 注册http和https请求
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslConnectionSocketFactory).build();

        //使用Httpclient连接池的方式配置(推荐),同时支持netty,okHttp以及其他http框架
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        // 最大连接数
        poolingHttpClientConnectionManager.setMaxTotal(1000);
        // 同路由并发数
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
        //配置连接池
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
        // 重试次数
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, true));
        //设置默认请求头
        List<Header> headers = new ArrayList<>();
        httpClientBuilder.setDefaultHeaders(headers);
        return httpClientBuilder.build();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

public ClientHttpRequestFactory clientHttpRequestFactory() {
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
    // 连接超时(毫秒),这里设置10秒
    clientHttpRequestFactory.setConnectTimeout(10 * 1000);
    // 数据读取超时时间(毫秒),这里设置60秒
    clientHttpRequestFactory.setReadTimeout(60 * 1000);
    // 从连接池获取请求连接的超时时间(毫秒),不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
    clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
    return clientHttpRequestFactory;
}

public RestTemplate restTemplate(){
    //创建RestTemplate的时候,指定ClientHttpRequestFactory
    return new RestTemplate(this.clientHttpRequestFactory());
}

@Test
public void test18() {
    RestTemplate restTemplate = this.restTemplate();
    String url = "http://localhost:8080/chat16/test/get";
    //getForObject方法,获取响应体,将其转换为第二个参数指定的类型
    BookDto bookDto = restTemplate.getForObject(url, BookDto.class);
    System.out.println(bookDto);
}

7、集成 okhttp

引入 maven 配置

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.3.1</version>
</dependency>

创建 RestTemplate

new RestTemplate(new OkHttp3ClientHttpRequestFactory());

8、总结

RestTemplate 使用确实非常容易,建议大家去看一下 RestTemplate 的源码,debug 跟踪一下过程,这样用起来就非常顺手了。

尚硅谷 Java 学科全套教程(总 207.77GB)

9、SpringMVC 系列目录

  1. SpringMVC 系列第 1 篇:helloword

  2. SpringMVC 系列第 2 篇:@Controller、@RequestMapping

  3. SpringMVC 系列第 3 篇:异常高效的一款接口测试利器

  4. SpringMVC 系列第 4 篇:controller 常见的接收参数的方式

  5. SpringMVC 系列第 5 篇:@RequestBody 大解密,说点你不知道的

  6. SpringMVC 系列第 6 篇:上传文件的 4 种方式,你都会么?

  7. SpringMVC 系列第 7 篇:SpringMVC 返回视图常见的 5 种方式,你会几种?

  8. SpringMVC 系列第 8 篇:返回 json & 通用返回值设计

  9. SpringMVC 系列第 9 篇:SpringMVC 返回 null 是什么意思?

  10. SpringMVC 系列第 10 篇:异步处理

  11. SpringMVC 系列第 11 篇:集成静态资源

  12. SpringMVC 系列第 12 篇:拦截器

  13. SpringMVC 系列第 13 篇:统一异常处理

  14. SpringMVC 系列第 14 篇:实战篇:通用返回值 & 异常处理设计

  15. SpringMVC 系列第 15 篇:全注解的方式  &  原理解析

  16. SpringMVC 系列第 16 篇:通过源码解析 SpringMVC 处理请求的流程

  17. SpringMVC 系列第 17 篇:源码解析 SpringMVC 容器的启动过程

  18. SpringMVC 系列第 18 篇:强大的 RequestBodyAdvice 解密

  19. SpringMVC 系列第 19 篇:强大的 ResponseBodyAdvice 解密

  20. SpringMVC 系列第 20 篇:RestFull 详解

10、更多好文章

  1. Spring 高手系列(共 56 篇)

  2. Java 高并发系列(共 34 篇)

  3. MySql 高手系列(共 27 篇)

  4. Maven 高手系列(共 10 篇)

  5. Mybatis 系列(共 12 篇)

  6. 聊聊 db 和缓存一致性常见的实现方式

  7. 接口幂等性这么重要,它是什么?怎么实现?

  8. 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

11、【路人甲 Java】所有系列高清 PDF

领取方式,扫码发送:yyds

84e19fb78bd1cedc2cba886ea5c699bc.png

原文地址:https://itsoku.blog.csdn.net

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具
文章浏览阅读2.1k次。初学Android游戏开发的朋友,往往会显得有些无所适从,他们常常不知道该从何处入手,每当遇到自己无法解决的难题时,又往往会一边羡慕于 iPhone下有诸如Cocos2d-iphone之类的免费游戏引擎可供使用,一边自暴自弃的抱怨Android平台游戏开发难度太高,又连个像样的游 戏引擎也没有,甚至误以为使用Java语言开发游戏是一件费力不讨好且没有出路的事情。事实上,这种想法完全是没有必_有素材的游戏引擎
文章浏览阅读3.2k次,点赞2次,收藏2次。2014年12月从csdn专家福利获得的一本书《Android游戏开发技术实战详解》,尘封了一年多的时间,今天才翻开来看。我认识中的Android,提到Android最先浮现在我脑海中的是那可爱的机器人图标:这个Logo是由Ascender公司设计的,诞生于2010年,其设计灵感源于男女厕所门上的图形符号(真的是灵感无处不在),于是布洛克绘制了一个简单的机器人,它的躯干就像锡罐的形状,头上还有两根_智能手机的特点有哪些?
文章浏览阅读8.1k次,点赞9次,收藏11次。首先,Android是不是真的找工作越来越难呢?这个可能是大家最关心的。这个受大的经济环境以及行业发展前景的影响,同时也和个人因素有关。2016-08-26近期一方面是所在的公司招聘Java开发人员很难招到合适的,投简历的人很少;而另一方面,经常听身边的人说Android、iOS方面找工作不好找,特别是没什么经验的,经验比较少的!说是不好找,但在我家所在的吉林省省会长春,会Unity3D+Maya_android 开发和asp.net哪个好 site:blog.csdn.net
文章浏览阅读6.1k次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;而如果没有手机或没有对应型号的手机,又想做对应型号(屏幕尺寸、Android系统版本)的适配,应该怎么办呢?这时Android模拟器就派上用场了。Android模拟器Android SDK自带一个移动模拟器。它是一个可以运行在你电脑上的_安卓移动开发软件怎样预览
文章浏览阅读8.9k次。Google IO 2017 上宣布,将Kotlin语言作为安卓开发的官方语言。Kotlin由JetBrains公司开发,与Java 100%互通,并具备诸多Java尚不支持的新特性。谷歌称还将与JetBrains公司合作,为Kotlin设立一个非盈利基金会。Kotlin 是一个基于 JVM 的静态类型编程语言,Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JV_kotlin为什么被嫌弃
文章浏览阅读9.6w次,点赞17次,收藏35次。有些情况下,不方便使用断点的方式来调试,而是希望在控制台打印输出日志,使用过Eclipse的同学都知道Java可以使用 System.out.println(""); 来在控制台打印输出日志,但是在android studio中却是不行的,还是有差别的,那应该用什么呢?android.util.Log在调试代码的时候我们需要查看调试信息,那我们就需要用Android Log类。android.ut_andirod.studio 为什么不在控制台打印输出
文章浏览阅读8.2k次,点赞2次,收藏8次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;这里讲一下如何为应用添加一个按钮,并为按钮添加Click单击事件处理程序,显示/隐藏另一个按钮。添加按钮在HelloWorld项目的基础上,打开界面布局文件:activity_main.xml切换到Design(设计)模式;在组件But_activity_main.xml按钮隐藏
文章浏览阅读2.9k次,点赞3次,收藏9次。android 开发工具主流的还是Android Studio,当然也有很多人喜欢用Eclipse,也有人喜欢用IntelliJ IDEA ;还有Xamarin这种只需要编写一次代码,可以编译多种平台可运行的强大工具。但是它又真的强大吗?就我看来没有,身边很多人还是在用Android Studio、XCode开发应用,没见谁在用Xamarin之类的工具。系统要求WindowsMicrosoft®_android开发下载安装
文章浏览阅读4.2k次,点赞7次,收藏26次。你知道Hello World程序的由来吗?对于大多数编程语言的学习来说,真正入门的一课就是 Hello World!会而不难,难而不会。虽然很多人写过关于Android开发Hello World的文章,但随着时间的推移,开发工具、技术的进步,可能有些已经过时了。我就记录一下当下我所经历的第一个Android APP HelloWorld。一、准备1、开发环境参考:Android Studio 下载_android helloworld textview 句柄获取
这篇“android轻量级无侵入式管理数据库自动升级组件怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定...
今天小编给大家分享一下Android实现自定义圆形进度条的常用方法有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...
这篇文章主要讲解了“Android如何解决字符对齐问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android...