HttpClient

知识概述

HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

HttpClient的主要功能:

  • 实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
  • 支持 HTTPS 协议
  • 支持代理服务器(Nginx等)等
  • 支持自动(跳转)转向

HttpClient入门案例

1.添加httpClient jar包

 <dependency>

         <groupId>org.apache.httpcomponents</groupId>

         <artifactId>httpclient</artifactId>

      </dependency>

 

     2.实现类

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);
        }
    }
    
}

 

GET无参

HttpClient发送示例:

@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();
            }
        }
    }

 对应接收示例:

 

 

GET有参(方式一:直接拼接URL):

HttpClient发送示例:

 

/**
     * 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有参(普通参数):

注: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();
            }
        }
    }

对应接收示例:

   Spring整合HttpClient

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);
    }

HttpClient调用过程

 

 

 

 

 

 

 

 

posted on 2019-11-12 20:19  棽犹  阅读(196)  评论(0编辑  收藏  举报

导航