在 Java 生態中,HttpClient 和 Feign 都是調用第三方接口的常用工具,但它們的定位、設計理念和使用場景有顯著差異。以下是詳細對比:
DIFF1. 定位與抽象層級
特性 | HttpClient | Feign |
---|---|---|
層級 | 底層 HTTP 客戶端庫(處理原始請求/響應) | 聲明式 HTTP 客戶端(基于接口和注解) |
核心目標 | 提供靈活的 HTTP 通信能力 | 簡化 REST 客戶端開發,隱藏 HTTP 細節 |
依賴關系 | 獨立使用(如 Apache HttpClient/OkHttp) | 需依賴 HttpClient(默認集成 Ribbon/OkHttp) |
DIFF2.性能與資源**
維度 | HttpClient | Feign |
---|---|---|
啟動開銷 | 低(直接使用) | 較高(需生成動態代理類) |
運行時性能 | 更優(無額外抽象層) | 輕微損耗(反射/動態代理) |
連接池 | 需手動配置 PoolingHttpClientConnectionManager | 自動復用底層 HttpClient 連接池 |
本文僅對HttpClient調用https接口調用做一個總結,先上代碼:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;/*** HTTPS 接口調用工具類* 包含 GET 和 POST 請求方法,支持自定義請求頭和跳過證書驗證(僅限測試環境)*/
public class HttpsClientUtil {/*** 發送 HTTPS GET 請求* @param url 請求地址* @param headers 請求頭數組,格式為 {"key1:value1", "key2:value2"}* @return 響應內容* @throws Exception 如果請求過程中發生錯誤*/public static String doGet(String url, String[] headers) throws Exception {// 1. 創建支持 HTTPS 的 HttpClient 實例try (CloseableHttpClient httpClient = createHttpsClient()) {// 2. 創建 GET 請求對象HttpGet httpGet = new HttpGet(url);// 3. 設置請求頭(如果有)if (headers != null) {for (String header : headers) {String[] kv = header.split(":");if (kv.length == 2) {httpGet.addHeader(kv[0].trim(), kv[1].trim());}}}// 4. 執行請求并獲取響應HttpResponse response = httpClient.execute(httpGet);// 5. 解析響應內容HttpEntity entity = response.getEntity();if (entity != null) {return EntityUtils.toString(entity, StandardCharsets.UTF_8);}return null;}}/*** 發送 HTTPS POST 請求(JSON格式)* @param url 請求地址* @param jsonBody JSON格式的請求體* @param headers 請求頭數組* @return 響應內容* @throws Exception 如果請求過程中發生錯誤*/public static String doPost(String url, String jsonBody, String[] headers) throws Exception {try (CloseableHttpClient httpClient = createHttpsClient()) {// 1. 創建 POST 請求對象HttpPost httpPost = new HttpPost(url);// 2. 設置請求頭和請求體httpPost.addHeader("Content-Type", "application/json");if (headers != null) {for (String header : headers) {String[] kv = header.split(":");if (kv.length == 2) {httpPost.addHeader(kv[0].trim(), kv[1].trim());}}}// 3. 設置 JSON 請求體if (jsonBody != null) {httpPost.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));}// 4. 執行請求并獲取響應HttpResponse response = httpClient.execute(httpPost);// 5. 解析響應內容HttpEntity entity = response.getEntity();if (entity != null) {return EntityUtils.toString(entity, StandardCharsets.UTF_8);}return null;}}/*** 創建支持 HTTPS 的 HttpClient 實例* 注意:此方法跳過證書驗證,僅適用于測試環境!* 生產環境應使用正確的證書驗證方式*/private static CloseableHttpClient createHttpsClient() throws Exception {// 1. 創建 SSLContext,配置信任所有證書(不安全,僅測試用)SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()) // 信任所有證書.build();// 2. 創建 SSL 連接工廠,跳過主機名驗證SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,NoopHostnameVerifier.INSTANCE); // 跳過主機名驗證// 3. 配置請求超時參數RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000) // 連接超時 5秒.setSocketTimeout(10000) // 請求超時 10秒.setConnectionRequestTimeout(2000) // 從連接池獲取連接超時 2秒.build();// 4. 創建 HttpClient 實例return HttpClients.custom().setSSLSocketFactory(sslSocketFactory) // 設置 SSL 工廠.setDefaultRequestConfig(requestConfig) // 設置超時配置.build();}/*** 測試方法*/public static void main(String[] args) {try {// 測試 GET 請求String getUrl = "https://jsonplaceholder.typicode.com/posts/1";String getResponse = doGet(getUrl, null);System.out.println("GET 響應:\n" + getResponse);// 測試 POST 請求String postUrl = "https://jsonplaceholder.typicode.com/posts";String jsonBody = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";String[] headers = {"Authorization: Bearer token123"};String postResponse = doPost(postUrl, jsonBody, headers);System.out.println("\nPOST 響應:\n" + postResponse);} catch (Exception e) {e.printStackTrace();}}
}
關鍵代碼解析
1. 創建信任所有證書的 SSLContext
SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()) // 信任所有證書.build();
TrustAllStrategy
是 Apache HttpClient 提供的策略,會信任所有證書(包括自簽名和過期證書)- 生產環境警告:這種配置不安全,只應在測試環境使用
2. 配置 SSL 連接工廠
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,NoopHostnameVerifier.INSTANCE); // 跳過主機名驗證
NoopHostnameVerifier
會跳過主機名驗證- 實際項目中應該使用
DefaultHostnameVerifier
3. 設置請求超時
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000) // 連接超時 5秒.setSocketTimeout(10000) // 請求超時 10秒.setConnectionRequestTimeout(2000) // 從連接池獲取連接超時 2秒.build();
- 防止因網絡問題導致線程長時間阻塞
4. 創建 HttpClient 實例
return HttpClients.custom().setSSLSocketFactory(sslSocketFactory) // 設置 SSL 工廠.setDefaultRequestConfig(requestConfig) // 設置超時配置.build();
- 使用 try-with-resources 確保資源自動關閉
生產環境建議
-
不要跳過證書驗證
應配置正確的信任庫:SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(new File("/path/to/truststore.jks"), "password".toCharArray()).build();
-
使用連接池
對于高頻請求,應配置連接池:PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(); connManager.setMaxTotal(100); connManager.setDefaultMaxPerRoute(20);
-
添加重試機制
對臨時性網絡錯誤實現自動重試 -
使用更現代的 HTTP 客戶端
考慮使用 OkHttp 或 Java 11+ 的 HttpClient
這個實現提供了完整的 HTTPS 調用功能,可以直接用于項目開發(測試環境)。