RestTemplate
是一个 HTTP 客户端,由 Spring 团队按照 RestFul 风格约束进行进一步抽象封装,便于开发者调用。webClient
, 并说明 RestTemplate 将在未来的版本中弃用,并且未来不会添加主要的新功能RestTemplate
如何使用,以及如何使用 Apache HttpClient 和 OKHttp去替换其内部实现本文适合阅读对象:项目中依然使用了 RestTemplate 作为客户端的开发人员。或者有需要使用 Apache HttpClient 或者 OKHttp 这一类 HTTP 工具库的开发人员。
内部实现:RestTemplate 默认使用 JDK 自带的 HttpURLConnection 作为底层 HTTP 客户端实现。它是一个同步阻塞库,每一个 Http 请求都会创建一个线程
RestTemplate 使用: 使用 发送 Http 请求非常简单。需要三步,引入依赖,设置配置 Bean,然后使用
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
@Configuration
public class ApplicationContextConfig {
@Bean
@ConditionalOnMissingBean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
@Slf4j
@RestController
@RequestMapping("/consumer")
public class OrderController {
public static String PAYMENT_URL = "http://localhost:8001";
@Autowired
private RestTemplate restTemplate;
@PostMapping("/payment/insert")
public AjaxResult<Void> callPaymentInsert() {
PaymentEntity payment = new PaymentEntity();
payment.setId(1L);
payment.setSerial("123456");
log.info("执行前-----------");
restTemplate.postForObject(PAYMENT_URL + "/payment/insertOne", payment, AjaxResult.class);
log.info("执行后-----------");
return new AjaxResult<>(200, "success");
}
}
既然 spring 团队已经不推介使用 RestTemplate
, 那么在 spring5 中力推的 webClient
HTTP 客户端应该如何使用?
内部实现: webClient 使用 Spring Reactive 框架内部提供的异步,非阻塞解决方案。
webClient 使用: 使用需要两步,引入依赖,替换 RestTemplate 内部 HTTP 实现,然后使用
webClient 作为新一代的 HTTP 客户端,不光支持非阻塞方法,也支持与 RestTemplate 类似的阻塞方案。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
@Slf4j
@RestController
@RequestMapping("/consumer")
public class OrderController {
public static String PAYMENT_URL = "http://localhost:8001";
@Autowired
private RestTemplate restTemplate;
@PostMapping("/payment/insert2")
public AjaxResult<Void> callPaymentInsert() {
PaymentEntity payment = new PaymentEntity();
payment.setId(1L);
payment.setSerial("123456");
log.info("执行前-----------");
Flux<AjaxResult> paymentEntityFlux = WebClient.create()
.method(HttpMethod.POST)
.uri(PAYMENT_URL + "/payment/insertOne")
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(payment))
.retrieve()
.bodyToFlux(AjaxResult.class);
log.info("执行后-----------");
paymentEntityFlux.subscribe(System.out::println);
return new AjaxResult<>(200, "success");
}
}
当然 webClient 也可以使用阻塞方式, 区别在于 body 方法 bodyToMono(),以及返回值
@Slf4j
@RestController
@RequestMapping("/consumer")
public class OrderController {
public static String PAYMENT_URL = "http://localhost:8001";
@PostMapping("/payment/insert3")
public AjaxResult<Void> callWebClientMono() {
PaymentEntity payment = new PaymentEntity();
payment.setId(1L);
payment.setSerial("123456");
log.info("执行前-----------");
Mono<AjaxResult> resp = WebClient.create()
.method(HttpMethod.POST)
.uri(PAYMENT_URL + "/payment/insertOne")
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(payment))
.retrieve()
.bodyToMono(AjaxResult.class);
System.out.println(resp.block());
log.info("执行后-----------");
return new AjaxResult<>(200, "success");
}
}
技术发展真的挺快,仅仅几年时间过去,项目中必用的 RestTemplate
竟然已经有点渐渐不再被人问津。好在其封装非常的抽象,非常方便我们去替换内部实现。
作为开发人员可以依旧使用熟悉的 restTemplate 去调用,而其内部实现被我们悄悄替换为更为高效的实现
.
从开发人员的反馈,和网上的各种 HTTP 客户端性能以及易用程度评测来看.
而 RestTemplate 内部由 HttpURLConnection 实现,效率不如 OkHttp , 所以我们可以使用 OkHttp 替换其 RestTemplate 的内部实现 HttpURLConnection,来优化 Http 效率
<dependency>
<groupId>com.squareup.okhttp3groupId>
<artifactId>okhttpartifactId>
<version>4.10.0version>
dependency>
@Configuration
public class ApplicationContextConfig {
@Bean
@ConditionalOnMissingBean
public RestTemplate getRestTemplate() {
return new RestTemplate(getOkHttpRequestFactory());
}
private ClientHttpRequestFactory getOkHttpRequestFactory() {
return new OkHttp3ClientHttpRequestFactory();
}
}
@Configuration
public class ApplicationContextConfig {
@Bean
@ConditionalOnMissingBean
public RestTemplate getRestTemplate() {
return new RestTemplate(getOkHttpRequestFactory());
}
private ClientHttpRequestFactory getOkHttpRequestFactory() {
// 线程池
ConnectionPool pool = new ConnectionPool(30, 300L, TimeUnit.MINUTES);
OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
.connectionPool(pool)
.connectTimeout(10L, TimeUnit.SECONDS)
.readTimeout(10L, TimeUnit.SECONDS)
.writeTimeout(10L, TimeUnit.SECONDS)
// .hostnameVerifier((hostname, session) -> true)
// 设置代理
// .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 9000)))
// 拦截器
// .addInterceptor()
.build();
return new OkHttp3ClientHttpRequestFactory(okHttpClient);
}
}
Apache HttpClient 的版本在 4+ 和 5+ 版本上有非常大的区别。目前 4+ 版本已经停止更新,网上相关文章基本都是使用的 4+版本。文末有关于 4 的阅读资料
我们使用 4+ 最新版本 4.5.13
,因为 5+ 版本无法替换,2 年多过去了,官方很可能不再打算支持
在常见的 4+版本中,替换 restTemplate 内核必须用到一个类 HttpComponentsClientHttpRequestFactory
,而它是在 Common HttpClient 的基础上封装而成,官方文档已经强烈不建议使用《4.3 ,所以市面上上关于 Apache HttpClient 版本低于 4.3 的 的实现都可以抛弃了。
由于它也是 HTTP 的标准实现,所以与 OKHttp 一样,替换 RestTemplate 内部实现,就像喝水一样简单,
<dependency>
<groupId>org.apache.httpcomponents.client5groupId>
<artifactId>httpclient5artifactId>
<version>5.1.2version>
dependency>
@Configuration
public class ApplicationContextConfig {
@Bean
@ConditionalOnMissingBean
public RestTemplate getRestTemplate() {
return new RestTemplate(getApacheHttpClientRequestFactory());
}
private ClientHttpRequestFactory getApacheHttpClientRequestFactory() {
BasicHttpClientConnectionManager connectionManager =
new BasicHttpClientConnectionManager();
HttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
}
在查阅 httpclient5 实现 RestTemplate 的过程中耗费了较长时间。
- 要实现 RestTemplate ,必须实现 RestTemplate 的 ClientHttpRequestFactory 接口 ,在 httpclient4 中 ,对这个接口的实现类是
HttpComponentsClientHttpRequestFactory
。- 在 httpclient5 中,官方团队 2 年过去了,还没有实现这个类
HttpComponentsClientHttpRequestFactory
, 因此导致了 httpclient5 无法实现 RestTemplate.- 目前看来官方并不打算重新实现这个类了,所以只能使用 4.5.13(4+最新版本)来实现