spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。
本篇介绍如何使用RestTemplate,以及在SpringBoot里面的配置和注入。
RestTemplate包含以下几个部分:
直接进行简单的使用
/**
* @author CodeWYX
* @date 2022/1/21 15:36
*/
public class RestTemplateTest {
public static void main(String[] args) {
RestTemplate restT = new RestTemplate();
//通过Jackson JSON processing library直接将返回值绑定到对象
List<User> user = restT.getForObject("http://localhost:8088/user", List.class);
System.out.println(Arrays.asList(user));
}
}
01.getForObject 不带参
private void getForObject(){
RestTemplate restT = new RestTemplate();
//通过Jackson JSON processing library直接将返回值绑定到对象
List<User> user = restT.getForObject(url, List.class);
System.out.println(Arrays.asList(user));
}
02.getForObject 带参
private void getForObject(){
RestTemplate restT = new RestTemplate();
//通过Jackson JSON processing library直接将返回值绑定到对象
User user = restT.getForObject(url+"/{id}", User.class,7);
System.out.println(Arrays.asList(user));
}
03.getForEntity 不带参
private void getForEntity(){
RestTemplate restT = new RestTemplate();
//通过Jackson JSON processing library直接将返回值绑定到对象
ResponseEntity<List> forEntity = restT.getForEntity(url, List.class);
HttpStatus code = forEntity.getStatusCode();
HttpHeaders headers = forEntity.getHeaders();
List<User> body = forEntity.getBody();
System.out.println("code"+code);
System.out.println("headers"+headers);
System.out.println("body"+Arrays.asList(body));
}
04.getForEntity 带参
private void getForEntity1(){
RestTemplate restT = new RestTemplate();
//通过Jackson JSON processing library直接将返回值绑定到对象
ResponseEntity<User> forEntity = restT.getForEntity(url+"/{id}", User.class,7);
HttpStatus code = forEntity.getStatusCode();
HttpHeaders headers = forEntity.getHeaders();
User body = forEntity.getBody();
System.out.println("code"+code);
System.out.println("headers"+headers);
System.out.println("body"+Arrays.asList(body));
}
01.postForObject
private void postForObject(){
RestTemplate restT = new RestTemplate();
User user = new User();
user.setName("hello");
user.setPassword("word");
User i = restT.postForObject(url, user, User.class);
System.out.println(i);
}
02.postForEntity
private void postForEntity(){
RestTemplate restT = new RestTemplate();
User user = new User();
user.setName("hello1");
user.setPassword("word2");
ResponseEntity<User> i = restT.postForEntity(url, user, User.class);
User body = i.getBody();
System.out.println(i);
System.out.println(body);
}
private String postUser() {
RestTemplate restT = new RestTemplate();
String url = this.url;
//设置Http的Header
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
//设置访问参数
HashMap<String, Object> params = new HashMap<>();
params.put("name", "aaaa");
params.put("password", "cascascasggreg");
//设置访问的Entity
HttpEntity entity = new HttpEntity<>(params, headers);
ResponseEntity<String> result = null;
try {
//发起一个POST请求
result = restT.exchange(url, HttpMethod.POST, entity, String.class);
String body = result.getBody();
System.out.println(body);
} catch (Exception e) {
System.out.println("失败: " + e.getMessage());
}
return null;
}
// 1-Content-Type
RequestEntity<User> requestEntity = RequestEntity
.post(new URI(uri))
.contentType(MediaType.APPLICATION_JSON)
.body(user);
// 2-Accept
RequestEntity<User> requestEntity = RequestEntity
.post(new URI(uri))
.accept(MediaType.APPLICATION_JSON)
.body(user);
// 3-Other
RequestEntity<User> requestEntity = RequestEntity
.post(new URI(uri))
.header("Authorization", "Basic " + base64Credentials)
.body(user);
创建HttpClientConfig
类,设置连接池大小、超时时间、重试机制等。配置如下:
/**
* @author CodeWYX
* @date 2022/1/21 17:27
*/
@Configuration
@EnableScheduling
public class HttpClientConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfig.class);
@Resource
private HttpClientProperties p;
@Bean
public PoolingHttpClientConnectionManager poolingConnectionManager() {
SSLContextBuilder builder = new SSLContextBuilder();
try {
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] arg0, String arg1) {
return true;
}
});
} catch (NoSuchAlgorithmException | KeyStoreException e) {
LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
}
SSLConnectionSocketFactory sslsf = null;
try {
sslsf = new SSLConnectionSocketFactory(builder.build());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
}
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create()
.register("https", sslsf)
.register("http", new PlainConnectionSocketFactory())
.build();
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolingConnectionManager.setMaxTotal(p.getMaxTotalConnections()); //最大连接数
poolingConnectionManager.setDefaultMaxPerRoute(p.getDefaultMaxPerRoute()); //同路由并发数
return poolingConnectionManager;
}
@Bean
public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
return new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext httpContext) {
HeaderElementIterator it = new BasicHeaderElementIterator
(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000;
}
}
return p.getDefaultKeepAliveTimeMillis();
}
};
}
@Bean
public CloseableHttpClient httpClient() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(p.getRequestTimeout())
.setConnectTimeout(p.getConnectTimeout())
.setSocketTimeout(p.getSocketTimeout()).build();
return HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(poolingConnectionManager())
.setKeepAliveStrategy(connectionKeepAliveStrategy())
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) // 重试次数
.build();
}
@Bean
public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) {
return new Runnable() {
@Override
@Scheduled(fixedDelay = 10000)
public void run() {
try {
if (connectionManager != null) {
LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections...");
connectionManager.closeExpiredConnections();
connectionManager.closeIdleConnections(p.getCloseIdleConnectionWaitTimeSecs(), TimeUnit.SECONDS);
} else {
LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised");
}
} catch (Exception e) {
LOGGER.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e);
}
}
};
}
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("poolScheduler");
scheduler.setPoolSize(50);
return scheduler;
}
}
然后再配置RestTemplateConfig类,使用之前配置好的CloseableHttpClient类注入,同时配置一些默认的消息转换器:
/**
* RestTemplate客户端连接池配置
* @author CodeWYX
* @date 2022/1/21 17:39
*/
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class RestTemplateConfig {
@Resource
private CloseableHttpClient httpClient;
@Bean
public RestTemplate restTemplate(MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("utf-8"));
messageConverters.add(stringHttpMessageConverter);
messageConverters.add(jackson2HttpMessageConverter);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
@Bean
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory rf = new HttpComponentsClientHttpRequestFactory();
rf.setHttpClient(httpClient);
return rf;
}
}
创建HttpClientProperties类
/**
* @author CodeWYX
* @date 2022/1/21 17:27
*/
@Component
@ConfigurationProperties(prefix = "httpclient")
public class HttpClientProperties {
/**
* 建立连接的超时时间
*/
private int connectTimeout = 20000;
/**
* 连接不够用的等待时间
*/
private int requestTimeout = 20000;
/**
* 每次请求等待返回的超时时间
*/
private int socketTimeout = 30000;
/**
* 每个主机最大连接数
*/
private int defaultMaxPerRoute = 100;
/**
* 最大连接数
*/
private int maxTotalConnections = 300;
/**
* 默认连接保持活跃的时间
*/
private int defaultKeepAliveTimeMillis = 20000;
/**
* 空闲连接生的存时间
*/
private int closeIdleConnectionWaitTimeSecs = 30;
public int getConnectTimeout() {
return connectTimeout;
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public int getRequestTimeout() {
return requestTimeout;
}
public void setRequestTimeout(int requestTimeout) {
this.requestTimeout = requestTimeout;
}
public int getSocketTimeout() {
return socketTimeout;
}
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
public int getDefaultMaxPerRoute() {
return defaultMaxPerRoute;
}
public void setDefaultMaxPerRoute(int defaultMaxPerRoute) {
this.defaultMaxPerRoute = defaultMaxPerRoute;
}
public int getMaxTotalConnections() {
return maxTotalConnections;
}
public void setMaxTotalConnections(int maxTotalConnections) {
this.maxTotalConnections = maxTotalConnections;
}
public int getDefaultKeepAliveTimeMillis() {
return defaultKeepAliveTimeMillis;
}
public void setDefaultKeepAliveTimeMillis(int defaultKeepAliveTimeMillis) {
this.defaultKeepAliveTimeMillis = defaultKeepAliveTimeMillis;
}
public int getCloseIdleConnectionWaitTimeSecs() {
return closeIdleConnectionWaitTimeSecs;
}
public void setCloseIdleConnectionWaitTimeSecs(int closeIdleConnectionWaitTimeSecs) {
this.closeIdleConnectionWaitTimeSecs = closeIdleConnectionWaitTimeSecs;
}
}
注意,如果没有apache的HttpClient类,需要在pom文件中添加:
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.3version>
dependency>
MultiValueMap<String, Object> multiPartBody = new LinkedMultiValueMap<>();
multiPartBody.add("file", new ClassPathResource("/tmp/user.txt"));
RequestEntity<MultiValueMap<String, Object>> requestEntity = RequestEntity
.post(uri)
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(multiPartBody);
// 小文件
RequestEntity requestEntity = RequestEntity.get(uri).build();
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class);
byte[] downloadContent = responseEntity.getBody();
// 大文件
ResponseExtractor<ResponseEntity<File>> responseExtractor = new ResponseExtractor<ResponseEntity<File>>() {
@Override
public ResponseEntity<File> extractData(ClientHttpResponse response) throws IOException {
File rcvFile = File.createTempFile("rcvFile", "zip");
FileCopyUtils.copy(response.getBody(), new FileOutputStream(rcvFile));
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(rcvFile);
}
};
File getFile = this.restTemplate.execute(targetUri, HttpMethod.GET, null, responseExtractor);
@Service
public class DeviceService {
private static final Logger logger = LoggerFactory.getLogger(DeviceService.class);
@Resource
private RestTemplate restTemplate;
}
// 开始推送消息
logger.info("解绑成功后推送消息给对应的POS机");
LoginParam loginParam = new LoginParam();
loginParam.setUsername(managerInfo.getUsername());
loginParam.setPassword(managerInfo.getPassword());
HttpBaseResponse r = restTemplate.postForObject(
p.getPosapiUrlPrefix() + "/notifyLogin", loginParam, HttpBaseResponse.class);
if (r.isSuccess()) {
logger.info("推送消息登录认证成功");
String token = (String) r.getData();
UnbindParam unbindParam = new UnbindParam();
unbindParam.setImei(pos.getImei());
unbindParam.setLocation(location);
// 设置HTTP Header信息
URI uri;
try {
uri = new URI(p.getPosapiUrlPrefix() + "/notify/unbind");
} catch (URISyntaxException e) {
logger.error("URI构建失败", e);
return 1;
}
RequestEntity<UnbindParam> requestEntity = RequestEntity
.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", token)
.body(unbindParam);
ResponseEntity<HttpBaseResponse> responseEntity = restTemplate.exchange(requestEntity, HttpBaseResponse.class);
HttpBaseResponse r2 = responseEntity.getBody();
if (r2.isSuccess()) {
logger.info("推送消息解绑网点成功");
} else {
logger.error("推送消息解绑网点失败,errmsg = " + r2.getMsg());
}
} else {
logger.error("推送消息登录认证失败");
}
/**
* @author CodeWYX
* @date 2022/1/21 17:27
*/
@Component
public class HttpUtil {
private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);
@Resource
private RestTemplate restTemplate;
private static HttpUtil httpUtil;
@PostConstruct
public void init(){
httpUtil = this;
httpUtil.restTemplate = this.restTemplate;
}
public static <T> String httpRequest(String url, HttpMethod method, HttpEntity<T> entity){
try {
ResponseEntity<String> result = httpUtil.restTemplate.exchange(url, method, entity, String.class);
return result.getBody();
} catch (Exception e) {
logger.error("请求失败: " + e.getMessage());
}
return null;
}
}