在Java Spring里提供了通过RestTemplate调用http 请求的方法,这里汇总了在RestTemplate的基础上通过apache httpclient控制http请求的方式。

示例代码

RequestFactoryBuilder.java

使用apache httpclient来构建并控制http链接相关信息:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

public class RequestFactoryBuilder {
private static HttpComponentsClientHttpRequestFactory clientHttpRequestFactory;

static {
// 长连接保持30秒
PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30,
TimeUnit.SECONDS);
// 总连接数
pollingConnectionManager.setMaxTotal(500);
// 同路由的并发数
pollingConnectionManager.setDefaultMaxPerRoute(500);

HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setConnectionManager(pollingConnectionManager);
// 重试次数,默认是3次,没有开启
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(2, true));
// 保持长连接配置,需要在头添加Keep-Alive
httpClientBuilder.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE);

List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader("User-Agent",
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36"));
headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6"));
headers.add(new BasicHeader("Connection", "keep-alive"));

httpClientBuilder.setDefaultHeaders(headers);

HttpClient httpClient = httpClientBuilder.build();

// httpClient连接配置,底层是配置RequestConfig
clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
// 连接超时
clientHttpRequestFactory.setConnectTimeout(5000);
// 数据读取超时时间,即SocketTimeout
clientHttpRequestFactory.setReadTimeout(5000);
// 连接不够用的等待时间,不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
clientHttpRequestFactory.setConnectionRequestTimeout(200);
// 缓冲请求数据,默认值是true。通过POST或者PUT大量发送数据时,建议将此属性更改为false,以免耗尽内存。
// clientHttpRequestFactory.setBufferRequestBody(false);
}

public static HttpComponentsClientHttpRequestFactory get() {
return clientHttpRequestFactory;
}
}

WebClient.java

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

public class WebClient {

private static final Logger LOGGER = Logger.getLogger(WebClient.class);

private static RestTemplate restTemplate;

static {
// 添加内容转换器
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
messageConverters.add(new MappingJackson2HttpMessageConverter());
messageConverters.add(new FormHttpMessageConverter());
messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
messageConverters.add(new ByteArrayHttpMessageConverter());

restTemplate = new RestTemplate(messageConverters);
restTemplate.setRequestFactory(RequestFactoryBuilder.get());
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());

LOGGER.info("WebClient初始化完成");
}

public static RestTemplate getClient() {
return restTemplate;
}
}

注意这里定义的HttpMessageConverter,可根据需要调整对应的HttpMessageConverter以及其顺序。

以上面代码为例,实际产生的http request header信息如下:

GET / HTTP/1.1
Accept: text/plain, application/json, application/xml, application/*+json, */*
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
Accept-Encoding: gzip,deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Connection: keep-alive

常用的HttpMessageConverter

下面是相关说明:

MessageConverter 说明
StringHttpMessageConverter T为String,可读取所有媒体类型(/), 从请求和响应读取/编写字符串。 默认情况下,它支持媒体类型 text/* 并使用文本/无格式内容类型编写。
FormHttpMessageConverter 从请求和响应读取/编写表单数据。默认情况下,它读取媒体类型 application/x-www-form-urlencoded 并将数据写入 MultiValueMap
MarshallingHttpMessageConverter T为Object,可读取text/xml和application/xml媒体类型请求, 使用 Spring 的 marshaller/un-marshaller 读取/编写 XML 数据。它转换媒体类型为 application/xml 的数据。
MappingJacksonHttpMessageConverter T为Object,可读取application/json, 使用 Jackson 的 ObjectMapper 读取/编写 JSON 数据。它转换媒体类型为 application/json 的数据。
AtomFeedHttpMessageConverter 使用 ROME 的 Feed API 读取/编写 ATOM 源。它转换媒体类型为 application/atom+xml 的数据。
RssChannelHttpMessageConverter 使用 ROME 的 feed API 读取/编写 RSS 源。它转换媒体类型为 application/rss+xml 的数据。
MappingJackson2XmlHttpMessageConverter T为Object,可读取text/xml和application/xml媒体类型请求, 从请求和响应读取/编写XML。默认情况下, 它转换媒体类型为application/xml, text/xml, and application/*+xml的数据
ByteArrayHttpMessageConverter T为byte[]类型,可读取/, 读取/编写二进制流,默认情绪下支持所有媒体类型(*/*), 它转换媒体类型为application/octet-stream的数据
Jaxb2RootElementHttpMessageConverter 通过JAXB2读写XML信息,将请求消息转换到标注XmlRootElement和XmlType注解的类中,T为Object,可读取text/xml和application/xml媒体类型请求,响应信息的媒体类型为text/xml或application/xml

Spring是如何寻找最佳的HttpMessageConverter

  1. 首先获取注册的所有HttpMessageConverter集合
  2. 然后客户端的请求header中寻找客户端可接收的类型,比如 Accept application/json,application/xml等,组成一个集合
  3. 所有的HttpMessageConverter 都有canRead和canWrite方法 返回值都是boolean,看这个HttpMessageConverter是否支持当前请求的读与写,读对应@RequestBody注解, 写对应@ResponseBody注解
  4. 遍历HttpMessageConverter集合与前面获取可接受类型进行匹配,如果匹配直接使用当前第一个匹配的HttpMessageConverter,然后return(一般是通过Accept和返回值对象的类型进行匹配)

参考