HttpClient 是 Apache Jakarta Common 下的子項目,能夠用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,而且它支持 HTTP 協議最新的版本和建議。java
1.添加httpClient jar包web
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
2.實現類spring
public class TestHttpClient { /** * 1.實例化httpClient對象 * 2.準備url請求地址 https://www.baidu.com/ * 3.封裝請求方式對象 GET/POST/PUT/DELETE * 4.發起http請求.獲取服務器響應. * 5.判斷返回值狀態碼信息 200. * 6.從響應對象中獲取服務器返回值數據. * @throws IOException * @throws ClientProtocolException */ @Test public void testGet() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); String url = "https://www.baidu.com"; HttpGet get = new HttpGet(url); CloseableHttpResponse response = client.execute(get); if(response.getStatusLine().getStatusCode() == 200) { //表示請求服務正確 HttpEntity entity = response.getEntity();//返回值實體對象 String result = EntityUtils.toString(entity, "UTF-8"); System.out.println(result); } } }
HttpClient發送示例:apache
@Test public void doGetTestOne() { // 得到Http客戶端(能夠理解爲:你得先有一個瀏覽器;注意:實際上HttpClient與瀏覽器是不同的) CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 建立Get請求 HttpGet httpGet = new HttpGet("http://localhost:12345/doGetControllerOne"); // 響應模型 CloseableHttpResponse response = null; try { // 由客戶端執行(發送)Get請求 response = httpClient.execute(httpGet); // 從響應模型中獲取響應實體 HttpEntity responseEntity = response.getEntity(); System.out.println("響應狀態爲:" + response.getStatusLine()); if (responseEntity != null) { System.out.println("響應內容長度爲:" + responseEntity.getContentLength()); System.out.println("響應內容爲:" + EntityUtils.toString(responseEntity)); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { // 釋放資源 if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } }
對應接收示例:編程
HttpClient發送示例:json
/** * GET---有參測試 (方式一:手動在url後面加上參數) * * @date 2018年7月13日 下午4:19:23 */ @Test public void doGetTestWayOne() { // 得到Http客戶端(能夠理解爲:你得先有一個瀏覽器;注意:實際上HttpClient與瀏覽器是不同的) CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 參數 StringBuffer params = new StringBuffer(); try { // 字符數據最好encoding如下;這樣一來,某些特殊字符才能傳過去(如:某人的名字就是「&」,不encoding的話,傳不過去) params.append("name=" + URLEncoder.encode("&", "utf-8")); params.append("&"); params.append("age=24"); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } // 建立Get請求 HttpGet httpGet = new HttpGet("http://localhost:12345/doGetControllerTwo" + "?" + params); // 響應模型 CloseableHttpResponse response = null; try { // 配置信息 RequestConfig requestConfig = RequestConfig.custom() // 設置鏈接超時時間(單位毫秒) .setConnectTimeout(5000) // 設置請求超時時間(單位毫秒) .setConnectionRequestTimeout(5000) // socket讀寫超時時間(單位毫秒) .setSocketTimeout(5000) // 設置是否容許重定向(默認爲true) .setRedirectsEnabled(true).build(); // 將上面的配置信息 運用到這個Get請求裏 httpGet.setConfig(requestConfig); // 由客戶端執行(發送)Get請求 response = httpClient.execute(httpGet); // 從響應模型中獲取響應實體 HttpEntity responseEntity = response.getEntity(); System.out.println("響應狀態爲:" + response.getStatusLine()); if (responseEntity != null) { System.out.println("響應內容長度爲:" + responseEntity.getContentLength()); System.out.println("響應內容爲:" + EntityUtils.toString(responseEntity)); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { // 釋放資源 if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } }
對應接收實例:瀏覽器
注:POST傳遞普通參數時,方式與GET同樣便可,這裏以直接在url後綴上參數的方式示例。服務器
HttpClient發送示例:多線程
@Test public void doPostTestFour() { // 得到Http客戶端(能夠理解爲:你得先有一個瀏覽器;注意:實際上HttpClient與瀏覽器是不同的) CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 參數 StringBuffer params = new StringBuffer(); try { // 字符數據最好encoding如下;這樣一來,某些特殊字符才能傳過去(如:某人的名字就是「&」,不encoding的話,傳不過去) params.append("name=" + URLEncoder.encode("&", "utf-8")); params.append("&"); params.append("age=24"); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } // 建立Post請求 HttpPost httpPost = new HttpPost("http://localhost:12345/doPostControllerFour" + "?" + params); // 設置ContentType(注:若是隻是傳普通參數的話,ContentType不必定非要用application/json) httpPost.setHeader("Content-Type", "application/json;charset=utf8"); // 響應模型 CloseableHttpResponse response = null; try { // 由客戶端執行(發送)Post請求 response = httpClient.execute(httpPost); // 從響應模型中獲取響應實體 HttpEntity responseEntity = response.getEntity(); System.out.println("響應狀態爲:" + response.getStatusLine()); if (responseEntity != null) { System.out.println("響應內容長度爲:" + responseEntity.getContentLength()); System.out.println("響應內容爲:" + EntityUtils.toString(responseEntity)); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { // 釋放資源 if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } }
對應接收示例:併發
1.編輯pro配置文件
#最大鏈接數
http.maxTotal = 1000
#併發數
http.defaultMaxPerRoute = 20
#建立鏈接的最長時間
http.connectTimeout=5000
#從鏈接池中獲取到鏈接的最長時間
http.connectionRequestTimeout=500
#數據傳輸的最長時間
http.socketTimeout=5000
#提交請求前測試鏈接是否可用
http.staleConnectionCheckEnabled=true
2.編輯配置類
package com.jt.config; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySource(value="classpath:/properties/httpClient.properties") public class HttpClientConfig { @Value("${http.maxTotal}") private Integer maxTotal; //最大鏈接數 @Value("${http.defaultMaxPerRoute}") private Integer defaultMaxPerRoute; //最大併發連接數 @Value("${http.connectTimeout}") private Integer connectTimeout; //建立連接的最大時間 @Value("${http.connectionRequestTimeout}") private Integer connectionRequestTimeout; //連接獲取超時時間 @Value("${http.socketTimeout}") private Integer socketTimeout; //數據傳輸最長時間 @Value("${http.staleConnectionCheckEnabled}") private boolean staleConnectionCheckEnabled; //提交時檢查連接是否可用 //定義httpClient連接池 @Bean(name="httpClientConnectionManager") public PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager() { PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(); manager.setMaxTotal(maxTotal); //設定最大連接數 manager.setDefaultMaxPerRoute(defaultMaxPerRoute); //設定併發連接數 return manager; } //定義HttpClient /** * 實例化鏈接池,設置鏈接池管理器。 * 這裏須要以參數形式注入上面實例化的鏈接池管理器 @Qualifier 指定bean標籤進行注入 */ @Bean(name = "httpClientBuilder") public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager")PoolingHttpClientConnectionManager httpClientConnectionManager){ //HttpClientBuilder中的構造方法被protected修飾,因此這裏不能直接使用new來實例化一個HttpClientBuilder,可使用HttpClientBuilder提供的靜態方法create()來獲取HttpClientBuilder對象 HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); httpClientBuilder.setConnectionManager(httpClientConnectionManager); return httpClientBuilder; } /** * 注入鏈接池,用於獲取httpClient * @param httpClientBuilder * @return */ @Bean public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){ return httpClientBuilder.build(); } /** * Builder是RequestConfig的一個內部類 * 經過RequestConfig的custom方法來獲取到一個Builder對象 * 設置builder的鏈接信息 * @return */ @Bean(name = "builder") public RequestConfig.Builder getBuilder(){ RequestConfig.Builder builder = RequestConfig.custom(); return builder.setConnectTimeout(connectTimeout) .setConnectionRequestTimeout(connectionRequestTimeout) .setSocketTimeout(socketTimeout) .setStaleConnectionCheckEnabled(staleConnectionCheckEnabled); } /** * 使用builder構建一個RequestConfig對象 * @param builder * @return */ @Bean public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){ return builder.build(); } }
3. 編輯關閉鏈接配置文件
package com.jt.config; import javax.annotation.PreDestroy; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.pool.PoolStats; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component //交給spring容器管理 public class HttpClientClose extends Thread{ @Autowired private PoolingHttpClientConnectionManager manage; private volatile boolean shutdown; //開關 volatitle表示多線程可變數據,一個線程修改,其餘線程當即修改 public HttpClientClose() { ///System.out.println("執行構造方法,實例化對象"); //線程開啓啓動 this.start(); } @Override public void run() { try { //若是服務沒有關閉,執行線程 while(!shutdown) { synchronized (this) { wait(5000); //等待5秒 //System.out.println("線程開始執行,關閉超時連接"); //關閉超時的連接 PoolStats stats = manage.getTotalStats(); int av = stats.getAvailable(); //獲取可用的線程數量 int pend = stats.getPending(); //獲取阻塞的線程數量 int lea = stats.getLeased(); //獲取當前正在使用的連接數量 int max = stats.getMax(); //System.out.println("max/"+max+": av/"+av+": pend/"+pend+": lea/"+lea); manage.closeExpiredConnections(); } } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(); } super.run(); } //關閉清理無效鏈接的線程 @PreDestroy //容器關閉時執行該方法. public void shutdown() { shutdown = true; synchronized (this) { //System.out.println("關閉所有連接!!"); notifyAll(); //所有從等待中喚醒.執行關閉操做; } } }
5. 編輯工具API
package com.jt.util; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; 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.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @Service public class HttpClientService { @Autowired private CloseableHttpClient httpClient; @Autowired private RequestConfig requestConfig; /** * 編輯工具API目的簡化代碼,實現鬆耦合 * * 做用:幫助用戶發起http請求,獲取正確的結果返回給用戶 * 參數設計: 1.用戶url地址 2.Map<參數名,參數值> 3.字符編碼 * * get請求方式: * 1.沒有參數時: http://www.baidu.com * 2.有參數時: http://www.baidu.com?key1=value1&key2=value2... * @param url * @param params * @param charset * @return */ public String doGet(String url,Map<String,String> params,String charset) { //1.校驗用戶是否傳遞字符編碼 if(StringUtils.isEmpty(charset)) { charset = "UTF-8"; } //2.封裝URL地址 // http://www.baidu.com?key1=value1&key2=value2&... if(params != null) { url = url + "?"; //map遍歷 for (Map.Entry<String,String> entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); url += key+"="+value+"&"; } //截去多餘的& url = url.substring(0, url.length()-1); } //3.定義httpGet請求對象 HttpGet httpGet = new HttpGet(url); //設定請求的超時時間 httpGet.setConfig(requestConfig); //定義返回值數據 String result = null; try { CloseableHttpResponse response = httpClient.execute(httpGet); if(response.getStatusLine().getStatusCode() == 200) { //表示請求正確 HttpEntity entity = response.getEntity(); result = EntityUtils.toString(entity,charset); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } return result; } //重載方法 public String doGet(String url) { return doGet(url, null, null); } public String doGet(String url,Map<String,String> params) { return doGet(url, params, null); } //實現httpClient POST提交 public String doPost(String url,Map<String,String> params,String charset){ String result = null; //1.定義請求類型 HttpPost post = new HttpPost(url); post.setConfig(requestConfig); //定義超時時間 //2.判斷字符集是否爲null if(StringUtils.isEmpty(charset)){ charset = "UTF-8"; } //3.判斷用戶是否傳遞參數 if(params !=null){ //3.2準備List集合信息 List<NameValuePair> parameters = new ArrayList<>(); //3.3將數據封裝到List集合中 for (Map.Entry<String,String> entry : params.entrySet()) { parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } //3.1模擬表單提交 try { UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters,charset); //採用u8編碼 //3.4將實體對象封裝到請求對象中 post.setEntity(formEntity); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } //4.發送請求 try { CloseableHttpResponse response = httpClient.execute(post); //4.1判斷返回值狀態 if(response.getStatusLine().getStatusCode() == 200) { //4.2表示請求成功 result = EntityUtils.toString(response.getEntity(),charset); }else{ System.out.println("獲取狀態碼信息:"+response.getStatusLine().getStatusCode()); throw new RuntimeException(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } public String doPost(String url){ return doPost(url, null, null); } public String doPost(String url,Map<String,String> params){ return doPost(url, params, null); } public String doPost(String url,String charset){ return doPost(url, null, charset); } }
6.編輯測試類調試程序
@Autowired private HttpClientService httpClient; @Test public void doGet() { //http://manage.jt.com/web/item/findItemById?itemId=1474391969 String url = "http://manage.jt.com/web/item/findItemById"; Map<String,String> params = new HashMap<>(); params.put("itemId", "562379"); String json = httpClient.doGet(url, params); System.out.println(json); }