02-Spring REST Template教程02
说明:本文主要整理了spring-boot中rest template的使用方法,通过对 依赖、生产、主要方法的简要介绍,读者可以较快掌握Spring Boot Rest客户端的简单编码,文章也针对实际代码编写过程中的一些进阶要求,做了简要说明,这里包括:泛型对象转换、复杂对象序列化及反序列化定制、依赖其他库、链接复用、优化策略。
代码学习仓库如下所示:
1 gradle依赖
spring-boot-starter-web依赖中已经包含了REST Template的相关依赖,这里只需要包含了spring-boot-starter-web依赖即可。
implementation 'org.springframework.boot:spring-boot-starter-web'
2 RestTemplate构造
Spring Boot 中没有自动配置 RestTemplate,但是Spring Boot 提供了 RestTemplateBuilder,下面代码是一个相关RestTemplate的简单装配,用户可以根据需求灵活设置RestTemplateBuilder设置先关参数。
- RestTemplateBuilder.build()
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return new RestTemplate();
//return builder.build();
}
3 RestTemplate常用方法
GET 请求
• getForObject() / getForEntity()
POST 请求
• postForObject() / postForEntity()
PUT 请求
• put()
DELETE 请求
• delete()
4 RestTemplate 进阶
4.1 解析泛型对象
URI uriGetAll = UriComponentsBuilder
.fromUriString("http://localhost:8081/coffee/")
.build(1);
ParameterizedTypeReference<List<Coffee>> ptr =
new ParameterizedTypeReference<List<Coffee>>() {};
ResponseEntity<List<Coffee>> list = restTemplate
.exchange(uriGetAll, HttpMethod.GET, null, ptr);
list.getBody().forEach(c -> cLog.info("GET-ALL Coffee: {}", c));
4.2 类型转换
某些时候对于复杂对象,json解析无法得到正确或者符合用户要求的结果,此时可以通过@JsonComponent注解标注的自定义解析器及序列化器完成相关对象的解析及序列化定制。
标准序列化器
package com.fwding.spring.resttemplate.httpcomponents.model;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.joda.money.Money;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
@JsonComponent
public class MoneySerializer extends StdSerializer<Money> {
protected MoneySerializer() {
super(Money.class);
}
@Override
public void serialize(Money money, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeNumber(money.getAmount());
}
}
标准解析器
package com.fwding.spring.resttemplate.httpcomponents.model;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
@JsonComponent
public class MoneyDeserializer extends StdDeserializer<Money> {
protected MoneyDeserializer() {
super(Money.class);
}
@Override
public Money deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return Money.of(CurrencyUnit.of("CNY"), p.getDecimalValue());
}
}
4.3 其他依赖库
- Apache HttpComponents
• HttpComponentsClientHttpRequestFactory - Netty
• Netty4ClientHttpRequestFactory - OkHttp
• OkHttp3ClientHttpRequestFactory
4.4 底层优化策略
连接管理理
- PoolingHttpClientConnectionManager
- KeepAlive 策略略
超时设置
- connectTimeout / readTimeout
SSL校验
- 证书检查策略略
4.5 连接复用
默认实现
• org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy
package geektime.spring.springbucks.customer.support;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.http.HttpResponse;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import java.util.Arrays;
public class CustomConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
private final long DEFAULT_SECONDS = 30;
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
return Arrays.asList(response.getHeaders(HTTP.CONN_KEEP_ALIVE))
.stream()
.filter(h -> StringUtils.equalsIgnoreCase(h.getName(), "timeout")
&& StringUtils.isNumeric(h.getValue()))
.findFirst()
.map(h -> NumberUtils.toLong(h.getValue(), DEFAULT_SECONDS))
.orElse(DEFAULT_SECONDS) * 1000;
}
}
6 RestTemplate完整样例演示
简单样例方法,仅针对spring rest template的常用方法作了说明和验证,这里需要结合上一个子项目02-01-rest controller+ swagger教程 进行配合演示。
package com.fwding.spring.resttemplate.httpcomponents;
import com.fwding.spring.resttemplate.httpcomponents.model.Coffee;
import com.fwding.spring.resttemplate.httpcomponents.model.NewCoffeeRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.List;
@SpringBootApplication
public class RestTemplateComplexDemoApplication implements ApplicationRunner {
private static final Logger cLog = LoggerFactory.getLogger(RestTemplateComplexDemoApplication.class);
@Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(RestTemplateComplexDemoApplication.class)
.bannerMode(Banner.Mode.OFF)
//设置web运行模式,这里设置为不启动
.web(WebApplicationType.NONE)
.run(args);
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return new RestTemplate();
//return builder.build();
}
@Override
public void run(ApplicationArguments args) throws Exception {
//GET-方式1-直接构造字符串
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:8081/coffee/{id}")
.build(1);
Coffee result = restTemplate.getForObject(uri, Coffee.class);
cLog.info("--1--> coffee result:" + result);
//GET-方式2-uri+变量方式
Coffee result2 =
restTemplate.getForObject("http://localhost:8081/coffee/{id}", Coffee.class, "2");
cLog.info("--2--> coffee result:" + result2);
//POST
NewCoffeeRequest request = new NewCoffeeRequest("MOCHA", 100L);
Coffee coffeePost = restTemplate.postForObject("http://localhost:8081/coffee/", request, Coffee.class);
cLog.info("--3--> coffee result:" + coffeePost);
//GET-泛型对象解析-LIST
URI uriGetAll = UriComponentsBuilder
.fromUriString("http://localhost:8081/coffee/")
.build(1);
ParameterizedTypeReference<List<Coffee>> ptr =
new ParameterizedTypeReference<List<Coffee>>() {};
ResponseEntity<List<Coffee>> list = restTemplate
.exchange(uriGetAll, HttpMethod.GET, null, ptr);
list.getBody().forEach(c -> cLog.info("GET-ALL Coffee: {}", c));
}
}