HTTP调用之编码实践
对于 HTTP
请求,读者应该都不陌生。打开浏览器,在地址栏中输入一个正确的网址即可获得响应内容,如图 5-5 所示。
在实际的服务调用中肯定不会返回一个页面,而是返回接口的响应内容,比较常见的是 JSON
格式的字符串,如图 5-6 所示。


那么不借助浏览器,在 Java
代码中该如何处理呢?接下来通过几个代码示例讲解在 Java
代码中如何发起 HTTP
请求和处理 HTTP
响应结果。
被调用端编码实现
先创建一个名称为 service-demo
的 Spring Boot
实例项目,将端口号设置为 8081
,然后分别创建 ltd.newbee.cloud.service
包和 ltd.newbee.cloud.web
包,用于分别存放业务层实现类和 REST
层的 Controller
类。
在 ltd.newbee.cloud.service
包中新建 HelloServiceImpl
类,代码如下:
package ltd.newbee.cloud.service;
import org.springframework.stereotype.Component;
@Component
public class HelloServiceImpl {
public String getName() {
return "service01";
}
}
定义了 getName()
方法,该方法的作用是返回一个字符串。
在 ltd.newbee.cloud.web
包中新建 HelloServiceController
类,代码如下:
package ltd.newbee.cloud.web;
import ltd.newbee.cloud.service.HelloServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloServiceController {
@Autowired
private HelloServiceImpl helloService;
@GetMapping("/hello")
public String hello() {
// 调用本地方法,并通过 HTTP 进行响应
return "hello from " + helloService.getName();
}
}
HelloServiceController
类使用 @RestController
注解,并不会返回视图对象。该类中定义了 hello()
方法,映射地址为 /hello
,在访问该地址后会返回一个字符串给调用端。
本节的代码主要用于功能展示,并没有演示复杂的功能逻辑。
接下来复制 service-demo
为 service-demo2
,并修改类名和配置文件中的端口号。这样就有了两个被调用端的实例,代码结构如图 5-7 所示。

编码完成后分别启动两个实例,启动成功后,可以分别访问两个接口地址:
http://localhost:8081/hello
http://localhost:8082/hello
如果项目中没有报错,并且访问结果如图 5-8 所示,则编码完成。
被调用端的编码和验证都已经完成。下面编写调用端的代码。

在 Java
项目开发中,向其他服务发起 HTTP
调用是常见的功能需求,编码实现时需要使用客户端工具或第三方提供的 HTTP
开发包。有很多常用的 HTTP
开发包供开发人员选择,如 Java
自带的 HttpUrlConnection
类、HttpClient
工具包、Spring
提供的 RestTemplate
工具和 WebClient
工具。
笔者将分别使用 HttpClient
、RestTemplate
、WebClient
来演示它们是如何对 HTTP
请求进行处理的。
使用HttpClient处理请求
新建一个名称为 request-demo
的 Spring Boot
实例项目,将端口号设置为 8083
。在 pom.xml
文件中添加 httpclient
的依赖配置,代码如下:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
之后创建 ltd.newbee.cloud.web
包,用于存放调用端所需的测试类。在 ltd.newbee.cloud.web
包中新建 ConsumerController
类,代码如下:
package ltd.newbee.cloud.web;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
public class ConsumerController {
private final String SERVICE_URL = "http://localhost:8081";
//private final String SERVICE_URL = "http://localhost:8082";
/**
* 使用 HttpClient 未处理 HTTP 请求
* @return
* @throws IOException
*/
@GetMapping("/httpClientTest")
public String httpClientTest() throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(SERVICE_URL + "/hello");
CloseableHttpResponse response = null;
try {
// 执行请求
response = httpClient.execute(httpGet);
// 判断返回状态码
if (response.getStatusLine().getStatusCode() == 200) {
String content = EntityUtils.toString(response.getEntity(), "UTF-8");
// 打印请求结果
System.out.println(content);
}
} finally {
if (response != null) {
response.close();
}
}
httpClient.close();
return "请求成功";
}
}
代码中定义了 SERVICE_URL
变量,用于存放请求地址。在 httpClientTest()
方法中,使用 HttpClient
工具对目标服务的接口进行了请求,并打印接收的请求结果。
编码完成后,依次启动 service-demo
、service-demo2
、request-demo
三个实例,启动成功后可以访问如下测试地址:
http://localhost:8083/httpClientTest
如果项目中没有报错,控制台打印出了此次的请求结果,则编码完成:
hello from service01
hello from service02
使用RestTemplate处理请求
RestTemplate
是 Spring
提供的一个 HTTP
请求工具,它提供了常见的 REST
请求方案的模板,简化了在 Java
代码中处理 HTTP
请求的编码过程。接下来笔者将使用 RestTemplate
工具来完成 HTTP 请求的处理。
依然在 request-demo
项目中进行编码。先创建 ltd.newbee.cloud.config
包,并新建 RestTemplate
的配置类,代码如下:
package ltd.newbee.cloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.converter.StringHttpMessageConverter;
import java.nio.charset.Charset;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
// UTF-8 编码设置
restTemplate.getMessageConverters().set(1,
new StringHttpMessageConverter(Charset.forName("UTF-8")));
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
// 超时时间为 10 秒
factory.setReadTimeout(10 * 1000);
// 超时时间为 5 秒
factory.setConnectTimeout(5 * 1000);
return factory;
}
}
然后在 ConsumerController
类中引入 RestTemplate
对象,并使用它来发起请求和处理请求回调结果,代码如下:
@Resource
private RestTemplate restTemplate;
/**
* 使用 RestTemplate 来处理 HTTP 请求
* @return
* @throws IOException
*/
@GetMapping("/restTemplateTest")
public String restTemplateTest() {
// 打印请求结果
System.out.println(restTemplate.getForObject(SERVICE_URL + "/hello", String.class));
return "请求成功";
}
在 restTemplateTest()
方法中,使用 RestTemplate
工具对目标服务的接口进行了请求,并打印接收的请求结果。相较于 HttpClient
工具,RestTemplate
工具编码更加简单,不管是发起请求还是请求回调的处理都做了很多封装,方便开发人员使用。
编码完成后,依次启动 service-demo
、service-demo2
、request-demo
三个实例,启动成功后可以访问如下测试地址:
http://localhost:8083/restTemplateTest
如果项目中没有报错,控制台打印出了此次的请求结果,则编码完成:
hello from service01
hello from service02
使用WebClient处理请求
WebClient
是从 Spring WebFlux 5.0
版本开始提供的一个非阻塞的基于响应式编程的进行 HTTP
请求的客户端工具。它的响应式编程基于 Reactor
。与 RestTemplate
工具类似,它们都是 Spring
官方提供的 HTTP
请求工具,方便开发人员进行网络编程。
只是二者有些许不同,RestTemplate
是阻塞式客户端,WebClient
是非阻塞式客户端,并且二者所依赖的 Servlet
环境不同,WebClient
是 Spring WebFlux
开发库的一部分,引入 starter
场景启动器时使用的依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
不用引入 spring-boot-starter-web
。使用 RestTemplate
工具直接引用 spring-boot-starter-web
即可。
复制 request-demo
为 request-demo2
,修改 pom.xml
文件中的 web
场景启动器为 spring-boot-starter-webflux
,之后新建 ConsumerController2
类,并新增如下代码:
package ltd.newbee.cloud.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@RestController
public class ConsumerController2 {
private final String SERVICE_URL = "http://localhost:8081";
//private final String SERVICE_URL = "http://localhost:8082";
private WebClient webClient = WebClient.builder()
.baseUrl(SERVICE_URL)
.build();
/**
* 使用 WebClient 处理 HTTP 请求
* @return
*/
@GetMapping("/webClientTest")
public String webClientTest() {
Mono<String> mono = webClient
.get() // GET 请求方式
.uri("/hello") // 请求地址
.retrieve() // 获取响应结果
.bodyToMono(String.class); // 响应结果转换
// 打印请求结果
mono.subscribe(result -> {
System.out.println(result);
});
return "请求成功";
}
}
在 webClientTest()
方法中,使用 WebClient
工具对目标服务的接口进行请求,并打印接收的请求结果。相较于 RestTemplate
工具,WebClient
工具的编码方式有所不同,可以应用函数式编程与流式 API,支持 Reactive
类型(Mono
和 Flux
)。
编码完成后,依次启动 service-demo
、service-demo2
、request-demo2
三个实例,启动成功后可以访问如下测试地址:
http://localhost:8084/webClientTest
如果项目中没有报错,控制台打印出了此次的请求结果,则编码完成:
hello from service01
hello from service02
最终代码目录结构如图 5-9 所示。
当然,读者也可以自行编码,分别创建四个 Spring Boot
项目并进行编码。笔者为了功能演示和源码整理,把所有代码放在了同一个工程里。
