目录
- 前言
- [一、RestTemplate 是什么?](#一、RestTemplate 是什么?)
- [二、RestTemplate 和 HttpClient 区别](#二、RestTemplate 和 HttpClient 区别)
- 三、二者核心区别
- [四、为什么很多 Spring Boot 项目喜欢用 RestTemplate?](#四、为什么很多 Spring Boot 项目喜欢用 RestTemplate?)
- [五、RestTemplate 的工作原理](#五、RestTemplate 的工作原理)
- [六、Spring Boot 如何引入 RestTemplate](#六、Spring Boot 如何引入 RestTemplate)
- [七、Maven 引入](#七、Maven 引入)
- [八、如何创建 RestTemplate](#八、如何创建 RestTemplate)
-
- [1.RestTemplate 创建](#1.RestTemplate 创建)
- [2.RestTemplate + Apache HttpClient 创建](#2.RestTemplate + Apache HttpClient 创建)
- 九、为什么要放到@Bean里?
- [十、RestTemplate 常用方法](#十、RestTemplate 常用方法)
- [十一、GET 请求示例](#十一、GET 请求示例)
-
- [1. 请求接口](#1. 请求接口)
- [2. Java对象](#2. Java对象)
- [3. GET调用](#3. GET调用)
- [十二、GET 带参数](#十二、GET 带参数)
- [十三、POST 请求示例](#十三、POST 请求示例)
-
- [1. 请求接口](#1. 请求接口)
- [2. 请求对象](#2. 请求对象)
- [3. POST调用](#3. POST调用)
- 十四、exchange()(推荐)
-
- [1.GET 示例](#1.GET 示例)
- [2.GET 请求 + token 示例](#2.GET 请求 + token 示例)
- [3. POST 示例](#3. POST 示例)
- [4.POST 请求 + token](#4.POST 请求 + token)
- 5.简单文件下载(byte[])示例
- [6. InputStream 文件下载(推荐)](#6. InputStream 文件下载(推荐))
- 十五、如何获取文件名、文件类型、判断请求成功?
- [十六、RestTemplate 支持哪些底层实现?](#十六、RestTemplate 支持哪些底层实现?)
- [十七、RestTemplate 缺点](#十七、RestTemplate 缺点)
- 十八、现在为什么还大量使用?
- [十九、RestTemplate vs WebClient](#十九、RestTemplate vs WebClient)
- [二十、什么时候用 RestTemplate?](#二十、什么时候用 RestTemplate?)
- [二十一、什么时候用 WebClient?](#二十一、什么时候用 WebClient?)
- 二十二、企业里真实情况
- 二十三、学习路线
- 二十四、一个完整实际案例
前言
RestTemplate 和 HttpClient 都可以用于发送 HTTP 请求,但它们定位不同。
可以把它们理解成:
HttpClient:底层 HTTP 通信工具RestTemplate:Spring 对HttpClient等工具的高级封装
一、RestTemplate 是什么?
RestTemplate 是 Spring 提供的一个:
- 同步阻塞式 HTTP 客户端
- 用于调用:
- 微服务接口
- 第三方 API
- 文件下载
- 外部系统通信
它本质上是:
text
Spring 对 HTTP 请求进行了再次封装
让你:
- 不用手动处理连接
- 不用自己解析响应流
- 不用自己转换 JSON
可以像调用普通方法一样发 HTTP 请求。
二、RestTemplate 和 HttpClient 区别
text
业务代码
↓
RestTemplate
↓
ClientHttpRequestFactory
↓
HttpClient / OkHttp / JDK HttpURLConnection
也就是说:
text
RestTemplate 是上层封装
HttpClient 是底层实现
三、二者核心区别
| 对比项 | RestTemplate | HttpClient |
|---|---|---|
| 所属 | Spring | Apache |
| 定位 | 高级HTTP工具 | 底层HTTP客户端 |
| 使用难度 | 简单 | 较复杂 |
| JSON转换 | 自动 | 手动 |
| 与Spring整合 | 非常好 | 一般 |
| 学习成本 | 低 | 高 |
| 适合 | 微服务调用 | 底层定制 |
| 是否阻塞 | 是 | 有同步和异步 |
| 推荐状态 | 官方已维护模式 | 仍常用 |
四、为什么很多 Spring Boot 项目喜欢用 RestTemplate?
因为它:
- 和 Spring 完美整合
- 非常简单
- 自动 JSON 转对象
- 支持拦截器
- 支持负载均衡
- 支持超时配置
- 支持统一异常处理
例如:
java
User user = restTemplate.getForObject(url, User.class);
就直接得到 Java 对象了。
不用:
- 创建连接
- 读取 InputStream
- Jackson 转换 JSON
五、RestTemplate 的工作原理
当你:
java
restTemplate.getForObject(...)
时,内部会:
text
1. 创建HTTP请求
2. 建立TCP连接
3. 发送请求
4. 获取响应
5. 自动解析JSON
6. 转换成Java对象
六、Spring Boot 如何引入 RestTemplate
其实:
text
spring-web
里已经包含了。
Spring Boot Web 项目一般天然就有。
七、Maven 引入
如果没有,可以引入:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这里面已经包含:
- Spring MVC
- RestTemplate
- Jackson
八、如何创建 RestTemplate
1.RestTemplate 创建
通常会放到配置类里。
java
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory =
new SimpleClientHttpRequestFactory();
// 连接超时
factory.setConnectTimeout(5000);
// 读取超时
factory.setReadTimeout(10000);
return new RestTemplate(factory);
}
}
然后:
java
@Autowired
private RestTemplate restTemplate;
即可使用。
2.RestTemplate + Apache HttpClient 创建
因为 RestTemplate 默认实现性能一般。
很多公司会:
text
RestTemplate + Apache HttpClient
获得:
- 连接池
- 更高性能
- 更稳定
示例:
java
@Bean
public RestTemplate restTemplate() {
RequestConfig config = RequestConfig.custom()
//建立TCP连接最大等待时间。例如:服务器不存在、网络不通
.setConnectTimeout(5000)
//读取超时。连接成功后服务器迟迟不返回数据,等待多久。
.setResponseTimeout(10000)
.build();
CloseableHttpClient httpClient =
HttpClients.custom()
.setDefaultRequestConfig(config)
.build();
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
九、为什么要放到@Bean里?
因为 Spring 会:
- 创建单例对象
- 放入IOC容器
- 全局复用
- 自动注入
这样整个项目只会有一个 RestTemplate。
十、RestTemplate 常用方法
| 方法 | 作用 |
|---|---|
| getForObject | GET请求返回对象 |
| getForEntity | GET返回完整响应 |
| postForObject | POST返回对象 |
| postForEntity | POST返回完整响应 |
| exchange | 最强大的通用方法 |
十一、GET 请求示例
1. 请求接口
假设:
text
GET http://localhost:8080/user/1
返回:
json
{
"id":1,
"name":"Tom"
}
2. Java对象
java
@Data
public class User {
private Long id;
private String name;
}
3. GET调用
java
@Autowired
private RestTemplate restTemplate;
public void testGet() {
String url = "http://localhost:8080/user/1";
User user = restTemplate.getForObject(url, User.class);
System.out.println(user);
}
十二、GET 带参数
方式1:拼接URL
java
String url = "http://localhost:8080/user?id=1";
方式2:占位符(推荐)
java
String url = "http://localhost:8080/user/{id}";
User user = restTemplate.getForObject(
url,
User.class,
1
);
等价于:
text
/user/1
十三、POST 请求示例
1. 请求接口
text
POST /user/add
请求体:
json
{
"name":"Tom"
}
2. 请求对象
java
@Data
public class UserReq {
private String name;
}
3. POST调用
java
public void testPost() {
String url = "http://localhost:8080/user/add";
UserReq req = new UserReq();
req.setName("Tom");
User user = restTemplate.postForObject(
url,
req,
User.class
);
System.out.println(user);
}
十四、exchange()(推荐)
实际项目中:
text
90% 会使用 exchange()
因为它:
- 支持所有 HTTP 方法
- 支持请求头
- 支持 token
- 支持文件上传
- 支持复杂请求
1.GET 示例
java
HttpHeaders headers = new HttpHeaders();
headers.add("token", "123456");
HttpEntity<Void> entity = new HttpEntity<>(headers);
ResponseEntity<User> response =
restTemplate.exchange(
"http://localhost:8080/user/1",
HttpMethod.GET,
entity,
User.class
);
User user = response.getBody();
2.GET 请求 + token 示例
java
@Autowired
private RestTemplate restTemplate;
public void test() {
String url = "http://localhost:8080/user/1";
// 1. 创建请求头
HttpHeaders headers = new HttpHeaders();
// 2. 设置 token
headers.set("Authorization", "Bearer abc123");
// 3. 封装 HttpEntity
HttpEntity<Void> entity =
new HttpEntity<>(headers);
// 4. 发起请求
ResponseEntity<String> response =
restTemplate.exchange(
url,
HttpMethod.GET,
entity,
String.class
);
// 5. 获取响应
String body = response.getBody();
System.out.println(body);
}
3. POST 示例
java
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
UserReq req = new UserReq();
req.setName("Tom");
HttpEntity<UserReq> entity =
new HttpEntity<>(req, headers);
ResponseEntity<User> response =
restTemplate.exchange(
"http://localhost:8080/user/add",
HttpMethod.POST,
entity,
User.class
);
User user = response.getBody();
4.POST 请求 + token
java
public void testPost() {
String url = "http://localhost:8080/order/create";
// 请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth("abc123");
// 请求体
Map<String, Object> body = new HashMap<>();
body.put("name", "Tom");
body.put("age", 18);
// 封装
HttpEntity<Map<String, Object>> entity =
new HttpEntity<>(body, headers);
// 请求
ResponseEntity<String> response =
restTemplate.exchange(
url,
HttpMethod.POST,
entity,
String.class
);
System.out.println(response.getBody());
}
setBearerAuth 是什么?
这个:
headers.setBearerAuth("abc123");
等价于:
Authorization: Bearer abc123
它是 Spring 专门提供的快捷方法。
如果不是 Bearer token 怎么办?
有些系统:
token: xxx
或者:
Authorization: xxx
那就:
headers.set("token", "abc123");
或者:
headers.set("Authorization", "abc123");
即可。
5.简单文件下载(byte[])示例
对于一些小文件可以使用字节数组来下载:
java
public void download() throws Exception {
String url =
"http://localhost:8080/file/test.pdf";
// 请求头
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth("abc123");
HttpEntity<Void> entity =
new HttpEntity<>(headers);
// 请求
ResponseEntity<byte[]> response =
restTemplate.exchange(
url,
HttpMethod.GET,
entity,
byte[].class
);
// 获取文件字节
byte[] bytes = response.getBody();
// 保存文件
Files.write(
Paths.get("D:/test.pdf"),
bytes
);
System.out.println("下载完成");
}
6. InputStream 文件下载(推荐)
上面的示例使用 byte[] 会一次性加载整个文件到内存,对于大文件 大文件:500MB 或者1GB 这样的文件 容易 OOM(内存溢出)。
更推荐下面的使用 Resource 和 inputStream 边读边写大文件,它不会一次性加载整个文件,更适合大文件
java
public void download2() throws Exception {
String url = "http://localhost:8080/file/big.zip";
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth("abc123");
HttpEntity<Void> entity =
new HttpEntity<>(headers);
ResponseEntity<Resource> response =
restTemplate.exchange(
url,
HttpMethod.GET,
entity,
Resource.class
);
// 获取输入流
InputStream inputStream =
response.getBody().getInputStream();
// 输出流
FileOutputStream out =
new FileOutputStream("D:/big.zip");
byte[] buffer = new byte[8192];
int len;
while ((len = inputStream.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
inputStream.close();
System.out.println("下载成功");
}
十五、如何获取文件名、文件类型、判断请求成功?
HTTP 响应头里通常有:
java
Content-Disposition
例如:
java
Content-Disposition:
attachment; filename=test.pdf
获取文件名:
java
String disposition = response.getHeaders().getFirst("Content-Disposition");
System.out.println(disposition);
获取 Content-Type:
java
MediaType contentType = response.getHeaders().getContentType();
//application/pdf
System.out.println(contentType);
如何判断下载成功:
java
if (response.getStatusCode() == HttpStatus.OK) {
System.out.println("成功");
}
十六、RestTemplate 支持哪些底层实现?
默认:
text
JDK HttpURLConnection
但也可以替换成:
- Apache HttpClient(最常见)
- OkHttp
例如:
java
HttpComponentsClientHttpRequestFactory
底层用 HttpClient。
十七、RestTemplate 缺点
Spring 官方现在:
text
已不再积极推荐 RestTemplate
而推荐:
text
WebClient
原因:
- RestTemplate 是阻塞式
- 高并发性能一般
- 不适合响应式
十八、现在为什么还大量使用?
因为:
- 老项目太多
- 简单稳定
- 易于维护
- 大量公司还在用
特别是:
text
传统 Spring Boot 项目
仍然大量使用。
十九、RestTemplate vs WebClient
| 对比 | RestTemplate | WebClient |
|---|---|---|
| 模型 | 同步阻塞 | 异步非阻塞 |
| 性能 | 一般 | 高 |
| 学习难度 | 简单 | 较高 |
| 使用广泛度 | 非常广 | 越来越广 |
| 推荐程度 | 维护模式 | 官方推荐 |
二十、什么时候用 RestTemplate?
适合:
- 公司老项目
- 普通微服务调用
- 中小并发
- 简单HTTP调用
二十一、什么时候用 WebClient?
适合:
- 高并发
- 网关
- 响应式系统
- 大量IO请求
二十二、企业里真实情况
很多公司:
text
老项目:RestTemplate
新项目:OpenFeign/WebClient
因为:
text
Feign 更适合微服务
例如:
java
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/user/{id}")
User get(@PathVariable Long id);
}
比 RestTemplate 更方便。
二十三、学习路线
建议学习顺序:
text
1. HTTP基础
2. RestTemplate
3. HttpClient
4. OpenFeign
5. WebClient
因为:
text
RestTemplate 是理解微服务调用最好的入门
二十四、一个完整实际案例
java
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
public User getUser(Long id) {
String url =
"http://localhost:8080/user/{id}";
HttpHeaders headers = new HttpHeaders();
headers.add("token", "abc123");
HttpEntity<Void> entity =
new HttpEntity<>(headers);
ResponseEntity<User> response =
restTemplate.exchange(
url,
HttpMethod.GET,
entity,
User.class,
id
);
return response.getBody();
}
}