前言
在Java开发中经常遇见需要调用第三方接口的情况,这种情况下往往需要一个接口调用工具,常用的有hutool restTemplate httpClient。但是不管哪一种,在使用便捷性方面都不太方便。不如自己再去封装。
常见的二进制提交,formdata提交等需要做不少的代码转换。
开始
引入核心依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
其它依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!-- log start -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
<!-- log end -->
封装工具类
1.UrlEntity
import java.util.HashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* url对象
*
* @author LiTing
* @date 2021/2/26 11:18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UrlEntity {
/** 基础url */
public String baseUrl;
/** url参数 */
public Map<String, Object> params = new HashMap<>();
public String toUrl() {
int i = 0;
StringBuilder text = new StringBuilder(baseUrl);
for (Map.Entry<String, Object> entry : params.entrySet()) {
if (i == 0) {
text = text.append("?" + entry.getKey() + "=" + entry.getValue());
} else {
text = text.append("&" + entry.getKey() + "=" + entry.getValue());
}
i++;
}
return text.toString();
}
public UrlEntity addParam(Map<String, Object> map) {
if (map != null) {
params.putAll(map);
}
return this;
}
}
2.HttpClientException
import lombok.Data;
/**
* 第三方服务调用异常
*
* @author LiTing
* @date 2021/6/8 10:43
*/
@Data
public class HttpClientException extends Exception {
public HttpClientException(String message) {
super(message);
}
}
3.IoUtil
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 统一的io处理工具类
*
* @author liting
*/
public class IoUtil {
/** 默认的字节读取大小 */
private static final int SIZE = 4 * 1024;
public static File inputStream2File(
InputStream inputStream, String targetFile, boolean closeStream) throws IOException {
File file = new File(targetFile);
OutputStream outStream = null;
try {
outStream = new FileOutputStream(file);
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
byte[] buffer = new byte[SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
outStream.flush();
} finally {
if (outStream != null) {
outStream.close();
}
if (closeStream && inputStream != null) {
inputStream.close();
}
}
return file;
}
/** 目的是检查目录存不存在,不存在则创建. */
public static void checkPath(String path) {
File file = new File(path);
if (file != null && file.isDirectory() && !file.exists()) {
file.mkdirs();
}
if (file == null || !file.exists()) {
file.mkdirs();
}
}
/** byte数组转文件 */
public static byte[] file2Byte(File file) throws IOException {
byte[] buffer;
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
try {
checkPath(file.getParent());
fis = new FileInputStream(file);
bos = new ByteArrayOutputStream();
byte[] b = new byte[SIZE];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
} finally {
if (fis != null) {
fis.close();
}
if (bos != null) {
bos.close();
}
}
buffer = bos.toByteArray();
return buffer;
}
}
4.UrlUtil
import java.util.HashMap;
import org.hhdbb.hhpclient.entity.UrlEntity;
/**
* url解析工具
*
* @author LiTing
* @date 2021/2/26 11:12
*/
public class UrlUtil {
/**
* 解析url
*
* @param url
* @return
*/
public static UrlEntity parse(String url) {
UrlEntity entity = new UrlEntity();
if (url == null) {
return entity;
}
url = url.trim();
if ("".equals(url)) {
return entity;
}
String[] urlParts = url.split("\\?");
entity.baseUrl = urlParts[0];
// 没有参数
if (urlParts.length == 1) {
return entity;
}
// 有参数
String[] params = urlParts[1].split("&");
entity.params = new HashMap<>(8);
for (String param : params) {
String[] keyValue = param.split("=");
entity.params.put(keyValue[0], keyValue[1]);
}
return entity;
}
}
5.SingletonUtil
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
/**
* 单例工具类
*
* @author LiTing
* @date 2021/3/26 18:22
*/
public class SingletonUtil {
private static volatile PoolingHttpClientConnectionManager poolingHttpClientConnectionManager;
private static volatile HttpClientBuilder httpClientBuilder;
private static RequestConfig globalConfig =
RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
private static Lock lock1 = new ReentrantLock();
private static Lock lock2 = new ReentrantLock();
public static ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
public static ExecutorService executorService =
new ThreadPoolExecutor(
5, 100, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), namedThreadFactory);
/** 保证全局只使用提个连接池管理对象 */
public static PoolingHttpClientConnectionManager
getSingletonPoolingHttpClientConnectionManager() {
if (poolingHttpClientConnectionManager == null) {
lock1.lock();
try {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setDefaultMaxPerRoute(800);
cm.setMaxTotal(1000);
poolingHttpClientConnectionManager = cm;
} finally {
lock1.unlock();
}
}
return poolingHttpClientConnectionManager;
}
/** 保证全局只通过一个builder去创建client */
public static HttpClientBuilder getSingletonHttpClientBuilder() {
if (httpClientBuilder == null) {
lock2.lock();
try {
httpClientBuilder =
HttpClients.custom()
.setConnectionManager(
SingletonUtil.getSingletonPoolingHttpClientConnectionManager ())
.setDefaultRequestConfig(globalConfig);
} finally {
lock2.unlock();
}
}
return httpClientBuilder;
}
}
开始构建
6.HttpClientUtil
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.hhdbb.hhpclient.entity.UrlEntity;
import org.hhdbb.hhpclient.exception.HttpClientException;
/**
* http请求工具类
*
* @author LiTing
* @date 2021/3/26 16:39
*/
@Slf4j
public class HttpClientUtil {
private static final String DEFAULT_CHARSET = "UTF-8";
private static class HttpClientUtilHolder {
private static HttpClientUtil httpClientUtil = new HttpClientUtil();
}
public static final HttpClientUtil getSingleInstance() {
return HttpClientUtilHolder.httpClientUtil;
}
public static void httpResponseToFile(CloseableHttpResponse httpResponse, String path)
throws IOException {
HttpEntity httpEntity = httpResponse.getEntity();
InputStream inStream = httpEntity.getContent();
IoUtil.inputStream2File(inStream, path, true);
}
/**
* 发送Get请求
*
* @param url
* @param header
* @param tClass
* @return T
* @throws
* @version V1.0.0
* @date 2021/3/26 17:06
*/
public static <T> T doGet(String url, Map<String, String> header, Class<T> tClass)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpGet get = new HttpGet(url);
setHeader(get, header);
return getResult(httpClient, get, tClass);
}
/**
* 发送Get请求
*
* @param url
* @param header
* @param tClass
* @return T
* @throws
* @version V1.0.0
* @date 2021/3/26 17:06
*/
public static <T> T doGetWithProxy(
String url, Map<String, String> header, Class<T> tClass, String ip, int port)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpGet get = new HttpGet(url);
RequestConfig c = get.getConfig();
if (c == null) {
c = RequestConfig.DEFAULT;
}
RequestConfig requestConfig = RequestConfig.copy(c).setProxy(new HttpHost(ip, port)).build();
get.setConfig(requestConfig);
setHeader(get, header);
return getResult(httpClient, get, tClass);
}
/**
* 发送get请求 带参
*
* @param url
* @param params
* @param header
* @param tClass
* @return T
* @throws
* @version V1.0.0
* @date 2021/3/26 17:06
*/
public static <T> T doGet(
String url, Map<String, Object> params, Map<String, String> header, Class<T> tClass)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpGet get = new HttpGet(dealUrl(url, params));
setHeader(get, header);
return getResult(httpClient, get, tClass);
}
/**
* 下载文件
*
* @param url
* @param header
* @param filePath
* @return void
* @throws
* @version V1.0.0
* @date 2021/3/26 17:06
*/
public static void doGetFile(String url, Map<String, String> header, String filePath)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpGet get = new HttpGet(url);
setHeader(get, header);
try (CloseableHttpResponse res = doExecute(httpClient, get)) {
httpResponseToFile(res, filePath);
} catch (IOException e) {
e.printStackTrace();
// 换成自己的统一异常处理方法,省得到处抛HttpClientException
throw new HttpClientException("HTTP工具类IO异常");
}
}
/**
* 获取文件输入流
*
* @param url
* @param header
* @return java.io.InputStream
* @throws
* @version V1.0.0
* @date 2021/3/26 17:05
*/
public static InputStream doGetFileInputStream(String url, Map<String, String> header)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpGet get = new HttpGet(url);
setHeader(get, header);
try (CloseableHttpResponse res = doExecute(httpClient, get)) {
return res.getEntity().getContent();
} catch (IOException e) {
e.printStackTrace();
// aa//换成自己的统一异常处理方法,省得到处抛HttpClientException
throw new HttpClientException("HTTP工具类IO异常");
}
}
/**
* 发送 application/x-www-form-urlencoded请求
*
* @param url
* @param param
* @param header
* @param tClass
* @return T
* @throws
* @version V1.0.0
* @date 2021/3/26 17:05
*/
public static <T> T doPost(
String url, Map<String, Object> param, Map<String, String> header, Class<T> tClass)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpPost httpPost = new HttpPost(url);
setHeader(httpPost, header);
try {
setUrlFormEntity(httpPost, param);
return getResult(httpClient, httpPost, tClass);
} catch (IOException e) {
e.printStackTrace();
// 换成自己的统一异常处理方法,省得到处抛HttpClientException
throw new HttpClientException("HTTP工具类IO异常");
}
}
/**
* 发送application/json请求
*
* @param url
* @param json
* @param header
* @param t
* @return T
* @throws
* @version V1.0.0
* @date 2021/3/26 17:04
*/
public static <T> T doPostJson(String url, String json, Map<String, String> header, Class<T> t)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpPost httpPost = new HttpPost(url);
setHeader(httpPost, header);
setJsonEntity(httpPost, json);
return getResult(httpClient, httpPost, t);
}
/**
* form表单文件上传
*
* @param url
* @param params
* @param headers
* @param t
* @return T
* @throws
* @version V1.0.0
* @date 2021/3/26 17:23
*/
public static <T> T doPostFormData(
String url, Map<String, Object> params, Map<String, String> headers, Class<T> t)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpPost httpPost = new HttpPost(url);
setHeader(httpPost, headers);
setFormDataEntity(httpPost, params);
return getResult(httpClient, httpPost, t);
}
/**
* 仅上传文件
*
* @param url
* @param file
* @param headers
* @param tClass
* @return T
* @throws
* @version V1.0.0
* @date 2021/3/26 17:30
*/
public static <T> T doPostFile(
String url, File file, Map<String, String> headers, Class<T> tClass)
throws HttpClientException {
CloseableHttpClient httpClient = getHttpClient();
HttpPost httpPost = new HttpPost(url);
setHeader(httpPost, headers);
ByteArrayEntity byteArrayEntity = null;
try {
if (file != null) {
byteArrayEntity = new ByteArrayEntity(IoUtil.file2Byte(file));
}
httpPost.setEntity(byteArrayEntity);
return getResult(httpClient, httpPost, tClass);
} catch (IOException e) {
e.printStackTrace();
// 换成自己的统一异常处理方法,省得到处抛HttpClientException
throw new HttpClientException("HTTP工具类IO异常");
}
}
public static CloseableHttpClient getHttpClient() {
return SingletonUtil.getSingletonHttpClientBuilder().build();
}
public static void setHeader(HttpRequestBase hb, Map<String, String> headers) {
if (headers != null) {
for (String key : headers.keySet()) {
hb.setHeader(key, headers.get(key));
}
}
}
private static CloseableHttpResponse doExecute(CloseableHttpClient hc, HttpRequestBase hb)
throws IOException {
CloseableHttpResponse ch = hc.execute(hb);
return ch;
}
private static String dealUrl(String url, Map<String, Object> params) {
UrlEntity urlEntity = UrlUtil.parse(url);
urlEntity.addParam(params);
return urlEntity.toUrl();
}
public static void setUrlFormEntity(HttpPost httpPost, Map<String, Object> param)
throws UnsupportedEncodingException {
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, String.valueOf(param.get(key))));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
}
public static void setJsonEntity(HttpPost httpPost, String json) {
StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8);
entity.setContentType("application/json;charset=utf-8");
httpPost.setEntity(entity);
}
public static void setFormDataEntity(HttpPost httpPost, Map<String, Object> params) {
MultipartEntityBuilder mEntityBuilder = MultipartEntityBuilder.create();
mEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
mEntityBuilder.setCharset(StandardCharsets.UTF_8);
ContentType textContent = ContentType.create("text/plain", StandardCharsets.UTF_8);
params.forEach(
(k, v) -> {
if (v instanceof File) {
File file = (File) v;
mEntityBuilder.addBinaryBody(k, file);
} else if (v instanceof Collection) {
Collection cl = (Collection) v;
cl.stream()
.forEach(
c -> {
File f = (File) c;
mEntityBuilder.addBinaryBody(k, f);
});
} else {
mEntityBuilder.addTextBody(k, v.toString(), textContent);
}
});
httpPost.setEntity(mEntityBuilder.build());
}
public static <T> boolean checkStringResult(Class<T> t) {
if (String.class.isAssignableFrom(t)) {
return true;
}
return false;
}
public static <T> boolean checkJsonObjectResult(Class<T> t) {
return JSONObject.class.isAssignableFrom(t);
}
public static <T> boolean checkJsonArrayResult(Class<T> t) {
return JSONArray.class.isAssignableFrom(t);
}
public static <T> T getResult(CloseableHttpClient hc, HttpRequestBase hb, Class<T> t)
throws HttpClientException {
String s;
try (CloseableHttpResponse res = doExecute(hc, hb)) {
s = EntityUtils.toString(res.getEntity(), DEFAULT_CHARSET);
log.info("URI:{}", hb.getURI().toString());
log.info("RESULT:{}", s);
if (checkStringResult(t)) {
return (T) s;
} else if (checkJsonObjectResult(t)) {
return (T) JSONObject.parse(s);
} else if (checkJsonArrayResult(t)) {
return (T) JSONArray.parse(s);
}
} catch (IOException e) {
e.printStackTrace();
// 换成自己的统一异常处理方法,省得到处抛HttpClientException
throw new HttpClientException("HTTP工具类IO异常");
}
return JSON.parseObject(s, t);
}
}