HttpClient实现跨域请求

   HttpClient实现跨域请求,我们也可以理解为不同系统间的接口调用,那么什么为跨域呢?可以总结为不同域名,不同端口或者相同域名不同端口的系统。分布式项目是现在市场的主流,跨域请求在项目中各子系统中的联系也是必不可少。熟悉的跨域请求技术一个jsonp,一个HttpClient,jsonp底层是通过<script>调用回调函数并传递参数实现跨域,但他只能实现get请求,有时间整理出来再与大家分享。废话不多说,下面一起探讨HttpClient。

   HttpClient其主要作用就是通过Http协议,向某个URL地址发起请求,并且获取响应结果,我们通过一个简单的案例就可以快速入门

1.1发起Get请求

public class DoGET {

 

    public static void main(String[] args) throws Exception {

        // 创建Httpclient对象,相当于打开了浏览器

        CloseableHttpClient httpclient = HttpClients.createDefault();

 

        // 创建HttpGet请求,相当于在浏览器输入地址

        HttpGet httpGet = new HttpGet("http://www.baidu.com/");

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求,相当于敲完地址后按下回车。获取响应

            response = httpclient.execute(httpGet);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                // 解析响应,获取数据

                String content = EntityUtils.toString(response.getEntity(), "UTF-8");

                System.out.println(content);

            }

        } finally {

            if (response != null) {

                // 关闭资源

                response.close();

            }

            // 关闭浏览器

            httpclient.close();

        }

 

    }

}


1.2带参数的get请求

public class DoGETParam {

 

    public static void main(String[] args) throws Exception {

        // 创建Httpclient对象

        CloseableHttpClient httpclient = HttpClients.createDefault();

        // 创建URI对象,并且设置请求参数

        URI uri = new URIBuilder("http://www.baidu.com/s").setParameter("wd", "java").build();

        // 创建http GET请求

        HttpGet httpGet = new HttpGet(uri);

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求

            response = httpclient.execute(httpGet);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                // 解析响应数据

                String content = EntityUtils.toString(response.getEntity(), "UTF-8");

                System.out.println(content);

            }

        } finally {

            if (response != null) {

                response.close();

            }

            httpclient.close();

        }

    }

}


1.3发起post请求

public class DoPOST {

    public static void main(String[] args) throws Exception {

        // 创建Httpclient对象

        CloseableHttpClient httpclient = HttpClients.createDefault();

        // 创建http POST请求

        HttpPost httpPost = new HttpPost("http://www.oschina.net/");

        // 把自己伪装成浏览器。否则开源中国会拦截访问

        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求

            response = httpclient.execute(httpPost);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                // 解析响应数据

                String content = EntityUtils.toString(response.getEntity(), "UTF-8");

                System.out.println(content);

            }

        } finally {

            if (response != null) {

                response.close();

            }

            // 关闭浏览器

            httpclient.close();

        }

 

    }

}


1.4带参数post请求

public class DoPOSTParam {

 

    public static void main(String[] args) throws Exception {

        // 创建Httpclient对象

        CloseableHttpClient httpclient = HttpClients.createDefault();

        // 创建http POST请求,访问开源中国

        HttpPost httpPost = new HttpPost("http://www.oschina.net/search");

 

        // 根据开源中国的请求需要,设置post请求参数

        List<NameValuePair> parameters = new ArrayList<NameValuePair>(0);

        parameters.add(new BasicNameValuePair("scope", "project"));

        parameters.add(new BasicNameValuePair("q", "java"));

        parameters.add(new BasicNameValuePair("fromerr", "8bDnUWwC"));

        // 构造一个form表单式的实体

        UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);

        // 将请求实体设置到httpPost对象中

        httpPost.setEntity(formEntity);

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求

            response = httpclient.execute(httpPost);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                // 解析响应体

                String content = EntityUtils.toString(response.getEntity(), "UTF-8");

                System.out.println(content);

            }

        } finally {

            if (response != null) {

                response.close();

            }

            // 关闭浏览器

            httpclient.close();

        }

    }

}

2.5使用连接池管理器

public class HttpConnectManager {

 

    public static void main(String[] args) throws Exception {

        // 构建连接池管理器对象

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

        // 设置最大连接数

        cm.setMaxTotal(200);

        // 设置每个主机地址的并发数

        cm.setDefaultMaxPerRoute(20);

 

        // 构建请求配置信息

        RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) // 创建连接的最长时间

                .setConnectionRequestTimeout(500) // 从连接池中获取到连接的最长时间

                .setSocketTimeout(10 * 1000) // 数据传输的最长时间

                .setStaleConnectionCheckEnabled(true) // 提交请求前测试连接是否可用

                .build();

 

        // 构建HttpClient对象,把连接池、请求配置对象作为参数

        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm)

                .setDefaultRequestConfig(config).build();

 

        doGet(httpClient);

        doGet(httpClient);

    }

 

    public static void doGet(CloseableHttpClient httpClient) throws Exception {

        // 创建http GET请求

        HttpGet httpGet = new HttpGet("http://www.baidu.com/");

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求

            response = httpClient.execute(httpGet);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                String content = EntityUtils.toString(response.getEntity(), "UTF-8");

                System.out.println("内容长度:" + content.length());

            }

        } finally {

            if (response != null) {

                response.close();

            }

            // 此处不能关闭httpClient,如果关闭httpClient,连接池也会销毁

            // httpClient.close();

        }

    }

}


注意:

1) 在案例中的配置相当重要,特别是各种有关超时时间的配置。如果不配置1,可能就会导致坏连接一直阻塞,影响其它请     求。或者导致无法获取结果,产生意想不到的错误。

2) HttpClient对象不要关闭,否则会导致整个连接池失效。

实际开发中,HttpClient虽然有连接池来管理连接。但是,如果访问的服务端已经断开了连接,作为客户端的HttpClient是不知道的。此时,池中的这个连接就是一个无效的连接。如果这样的连接很多,就会造成有效连接数的减少,服务器的压力变大,直至崩溃。因此,定期清理无效的链接显得尤为重要。

定期清理无效链接

public class ClientEvictExpiredConnections {

 

    public static void main(String[] args) throws Exception {

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

        // 设置最大连接数

        cm.setMaxTotal(200);

        // 设置每个主机地址的并发数

        cm.setDefaultMaxPerRoute(20);

        // 开启线程,执行清理链接的任务

        new IdleConnectionEvictor(cm).start();

    }

    // 创建内部类,继承Thread,成为线程类

    public static class IdleConnectionEvictor extends Thread {

 

        private final HttpClientConnectionManager connMgr;

 

        private volatile boolean shutdown;

 

        public IdleConnectionEvictor(HttpClientConnectionManager connMgr) {

            this.connMgr = connMgr;

        }

        // 定义线程任务,定时关闭失效的连接

        @Override

        public void run() {

            try {

                while (!shutdown) {

                    synchronized (this) {

                        // 每隔5秒钟一次

                        wait(5000);

                        // 关闭失效的连接

                        connMgr.closeExpiredConnections();

                    }

                }

            } catch (InterruptedException ex) {

                // 结束

            }

        }

 

        public void shutdown() {

            shutdown = true;

            synchronized (this) {

                notifyAll();

            }

        }

    }

}


SSM中整合HttpClient

要在项目中整合HttpClient,其实就是把HttpClient注册到Spring容器中。

而一般注册一个Bean,也有两种方式:XML配置或者注解。

我们选哪种?
方式1,XML配置

l 新建applicationContext-httpClient.xml文件:

l 编写配置

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"

xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd

http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

 

<!-- 配置连接池管理器 -->

<bean id="httpClientConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager">

<!-- 配置最大连接数 -->

<property name="maxTotal" value="${http.maxTotal}" />

<!-- 配置每个主机地址的最大连接数 -->

<property name="defaultMaxPerRoute" value="${http.defaultMaxPerRoute}" />

</bean>

<!-- 配置RequestConfigBuilder -->

<bean id="requestConfigBuilder" class="org.apache.http.client.config.RequestConfig.Builder">

         <!-- 创建连接的最长时间 -->

         <property name="connectTimeout" value="${http.connectTimeout}" />

         <!-- 从连接池中获取到连接的最长时间 -->

         <property name="connectionRequestTimeout" value="${http.connectionRequestTimeout}" />

         <!-- 数据传输的最长时间 -->

         <property name="socketTimeout" value="${http.socketTimeout}" />

         <!-- 提交请求前测试连接是否可用 -->

         <property name="staleConnectionCheckEnabled" value="${http.staleConnectionCheckEnabled}" />

</bean>

<!-- 配置RequestConfig -->

<bean id="requestConfig" factory-bean="requestConfigBuilder" factory-method="build"/>

<!-- 配置HttpClientBuilder -->

<bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder">

<!-- 注入连接池管理器 -->

<property name="connectionManager" ref="httpClientConnectionManager" />

<!-- 注入默认的请求配置对象 -->

<property name="defaultRequestConfig" ref="requestConfig" />

</bean>

 

<!-- 配置HttpClient -->

<bean factory-bean="httpClientBuilder" factory-method="build"></bean>

</beans>


注意,在上面的配置中,引用了${},外部属性文件。所以,在Spring的核心配置中添加配置文件:appliationContext.xml中

l httpClient.properties :资源文件放在项目的根目录下 resource

l 定义一个HttpClient的通用Service:提供get请求和POST请求的方法

@Service

public class XmlApiService {

    // 注入在XML中已经配置好的HttpClient对象

    @Autowired

    private CloseableHttpClient httpClient;

    

    /**

     * 无参的get请求

     */

    public String doGet(String uri) throws IOException {

        // 创建HttpGet请求,相当于在浏览器输入地址

        HttpGet httpGet = new HttpGet(uri);

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求,相当于敲完地址后按下回车。获取响应

            response = httpClient.execute(httpGet);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                // 解析响应,获取数据

                return EntityUtils.toString(response.getEntity(), "UTF-8");

            }

        } finally {

            if (response != null) {

                response.close();

            }

        }

        return null;

    }

 

    /**

     * 有参get请求

     */

    public String doGet(String uri, Map<String, String> params) throws Exception {

        // 创建地址构建器

        URIBuilder builder = new URIBuilder(uri);

        // 拼接参数

        for (Map.Entry<String, String> me : params.entrySet()) {

            builder.addParameter(me.getKey(), me.getValue());

        }

        return doGet(builder.build().toString());

 

    }

 

    /**

     * 有参POST请求

     */

    public String doPost(String uri, Map<String, String> params) throws ParseException, IOException {

        // 创建http POST请求

        HttpPost httpPost = new HttpPost(uri);

        httpPost.setHeader("User-Agent",

                "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");

        if (params != null) {

            // 设置参数

            List<NameValuePair> parameters = new ArrayList<NameValuePair>(0);

            for (Map.Entry<String, String> me : params.entrySet()) {

                parameters.add(new BasicNameValuePair(me.getKey(), me.getValue()));

            }

            // 构造一个form表单式的实体

            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);

            // 将请求实体设置到httpPost对象中

            httpPost.setEntity(formEntity);

        }

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求

            response = httpClient.execute(httpPost);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                String content = EntityUtils.toString(response.getEntity(), "UTF-8");

                System.out.println(content);

            }

        } finally {

            if (response != null) {

                response.close();

            }

        }

        return null;

    }

 

    /**

     * 无参POST请求

     */

    public String doPost(String uri) throws ParseException, IOException {

        return doPost(uri, null);

    }

}

 

l 先把清理无效连接的线程复制到项目中:

注意修改构造函数,在构造函数中启动当前线程任务,这样当Bean一旦注册,线程就启动了

/**

 * 定时清理无效链接的线程

 */

public class IdleConnectionEvictor extends Thread {

 

    private final HttpClientConnectionManager connMgr;

 

    private volatile boolean shutdown;

 

    public IdleConnectionEvictor(HttpClientConnectionManager connMgr) {

        this.connMgr = connMgr;

        // 启动线程

        this.start();

    }

 

    // 定义线程任务,定时关闭失效的连接

    @Override

    public void run() {

        try {

            while (!shutdown) {

                synchronized (this) {

                    // 每隔5秒钟一次

                    wait(5000);

                    // 关闭失效的连接

                    connMgr.closeExpiredConnections();

                }

            }

        } catch (InterruptedException ex) {

            // 结束

        }

        System.out.println("清理任务 即将结束。。");

    }

 

    public void shutdown() {

        shutdown = true;

        synchronized (this) {

            notifyAll();

        }

    }

}

l 在applicationContext-httpclient.xml配置文件中注册该类:

<bean class="com.taotao.common.bean.IdleConnectionEvictor" destroy-method="shutdown">

<constructor-arg index="0" ref="httpClientConnectionManager"></constructor-arg>

</bean>

 

l 完整的applicationContext-httpclient.xml:

 

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"

xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd

http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

   

<!-- 设置请求参数的配置 -->

<bean id="configBuilder" class="org.apache.http.client.config.RequestConfig.Builder">

<!-- 创建连接的最长时间 -->

<property name="connectTimeout" value="${httpclient.connectTimeout}"></property>

<!-- 从连接池中获取到连接的最长时间 -->

<property name="connectionRequestTimeout" value="${httpclient.connectionRequestTimeout}"></property>

<!-- 数据传输的最长时间 -->

<property name="socketTimeout" value="${httpclient.socketTimeout}"></property>

<!-- 提交请求前测试连接是否可用 -->

<property name="staleConnectionCheckEnabled" value="${httpclient.staleConnectionCheckEnabled}"></property>

</bean>

<!-- 配置RequestConfig -->

<bean  id="requestConfig" factory-bean="configBuilder" factory-method="build"></bean>

 <!-- httpclient连接管理器 -->

<bean id="httpClientConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager">

<!-- 最大连接数 -->

<property name="maxTotal" value="${httpclient.maxTotal}"></property>

<!-- 设置每个主机地址的并发数 -->

<property name="defaultMaxPerRoute" value="${httpclient.defaultMaxPerRoute}"></property>

</bean>

     <!-- httpclient的构造器 -->

<bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder">

<property name="connectionManager" ref="httpClientConnectionManager"></property>

<!-- 注入默认的请求配置对象 -->

<property name="defaultRequestConfig" ref="requestConfig" />

</bean>

    <!-- 配置httpclient对象 scope="prototype" : 默认是单例,我们改成prototype,每次重新创建一个实例-->

<bean class="org.apache.http.impl.client.CloseableHttpClient"

 factory-bean="httpClientBuilder" factory-method="build" >

 </bean>

<!-- 自定义定期关闭无效HttpClient连接的类 -->

<bean class="com.taotao.common.bean.IdleConnectionEvictor" destroy-method="shutdown">

<constructor-arg index="0" ref="httpClientConnectionManager"></constructor-arg>

</bean>

</beans>


l 测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = "classpath:spring/applicationContext*.xml")

public class ApiServiceTest {

 

    @Autowired

    private XmlApiService apiService;

    

    @Test

    public void testDoGetString() throws IOException {

        String content = this.apiService.doGet("http://www.baidu.com");

        System.out.println(content);

    }

 

    @Test

    public void testDoGetStringMapOfStringString() throws Exception {

        Map<String,String> params = new HashMap<>();

        params.put("categoryId", "13");

        params.put("rows", "6");

        params.put("callback", "func");

        String content = this.apiService.doGet("http://manage.taotao.com/rest/api/content",params);

        System.out.println(content);

    }

 

    @Test

    public void testDoPostStringMapOfStringString() throws ParseException, IOException {

 

        Map<String,String> params = new HashMap<>();

        params.put("scope", "project");

        params.put("q", "java");

        params.put("fromerr", "8bDnUWwC");

        

        String content = this.apiService.doPost("http://www.oschina.net/search", params);

        System.out.println(content);

    }

 

    @Test

    public void testDoPostString() {

        fail("Not yet implemented");

    }

 

}


一切OK,整合成功

方式2:注解方式

直接在APIService中,通过@value注解来注入 配置参数。

然后通过@PostConstructor 注解来声明一个初始化方法,在这个方法中完成HttpClient对象的创建。

/**

 * 通过HttpClient进行远程调用的Service

 */

@Service

public class ApiService {

 

    @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;

 

    private CloseableHttpClient httpClient;

 

    // PostConstruct注解标明该方法为初始化方法,会在对象构造函数执行完毕后执行

    @PostConstruct

    public void init() {

        // 构建连接池管理器对象

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

        // 设置最大连接数

        cm.setMaxTotal(maxTotal);

        // 设置每个主机地址的并发数

        cm.setDefaultMaxPerRoute(defaultMaxPerRoute);

 

        // 构建请求配置信息

        RequestConfig config = RequestConfig.custom().setConnectTimeout(connectTimeout) // 创建连接的最长时间

                .setConnectionRequestTimeout(connectionRequestTimeout) // 从连接池中获取到连接的最长时间

                .setSocketTimeout(socketTimeout) // 数据传输的最长时间

                .setStaleConnectionCheckEnabled(staleConnectionCheckEnabled) // 提交请求前测试连接是否可用

                .build();

 

        // 构建HttpClient对象,把连接池、请求配置对象作为参数

        httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(config).build();

    }

 

    /**

     * 无参的get请求

     */

    public String doGet(String uri) throws IOException {

        // 创建HttpGet请求,相当于在浏览器输入地址

        HttpGet httpGet = new HttpGet(uri);

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求,相当于敲完地址后按下回车。获取响应

            response = httpClient.execute(httpGet);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                // 解析响应,获取数据

                return EntityUtils.toString(response.getEntity(), "UTF-8");

            }

        } finally {

            if (response != null) {

                response.close();

            }

        }

        return null;

    }

 

    /**

     * 有参get请求

     */

    public String doGet(String uri, Map<String, String> params) throws Exception {

        // 创建地址构建器

        URIBuilder builder = new URIBuilder(uri);

        // 拼接参数

        for (Map.Entry<String, String> me : params.entrySet()) {

            builder.addParameter(me.getKey(), me.getValue());

        }

        return doGet(builder.build().toString());

 

    }

 

    /**

     * 有参POST请求

     */

    public String doPost(String uri, Map<String, String> params) throws ParseException, IOException {

        // 创建http POST请求

        HttpPost httpPost = new HttpPost(uri);

        httpPost.setHeader("User-Agent",

                "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");

        if (params != null) {

            // 设置参数

            List<NameValuePair> parameters = new ArrayList<NameValuePair>(0);

            for (Map.Entry<String, String> me : params.entrySet()) {

                parameters.add(new BasicNameValuePair(me.getKey(), me.getValue()));

            }

            // 构造一个form表单式的实体

            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);

            // 将请求实体设置到httpPost对象中

            httpPost.setEntity(formEntity);

        }

 

        CloseableHttpResponse response = null;

        try {

            // 执行请求

            response = httpClient.execute(httpPost);

            // 判断返回状态是否为200

            if (response.getStatusLine().getStatusCode() == 200) {

                String content = EntityUtils.toString(response.getEntity(), "UTF-8");

                System.out.println(content);

            }

        } finally {

            if (response != null) {

                response.close();

            }

        }

        return null;

    }

 

    /**

     * 无参POST请求

     */

    public String doPost(String uri) throws ParseException, IOException {

        return doPost(uri, null);

    }

}

 

 

 
添加定时清理无效连接的任务

 

我们这里按照方式2,也就是注解注入的方式来添加定时清理无效链接的任务。

 

l 首先,在ApiService类中,定义内部类,实现 Runnable接口,成为任务类,编写定时清理的线程任务

 

    // 创建内部类,实现Runnable接口,成为线程任务类

    private class IdleConnectionEvictor implements Runnable {

 

        private final HttpClientConnectionManager connMgr;

 

        private volatile boolean shutdown;

 

        public IdleConnectionEvictor(HttpClientConnectionManager connMgr) {

            this.connMgr = connMgr;

        }

 

        // 定义线程任务,定时关闭失效的连接

        @Override

        public void run() {

            try {

                while (!shutdown) {

                    synchronized (this) {

                        // 每隔5秒钟一次

                        wait(5000);

                        // 关闭失效的连接

                        connMgr.closeExpiredConnections();

                    }

                }

            } catch (InterruptedException ex) {

                // 结束

            }

        }

 

        public void shutdown() {

            shutdown = true;

            synchronized (this) {

                notifyAll();

                System.out.println("定时清理任务即将结束。。");

            }

        }

    }


l 在初始化方法,init中,HttpClient对象创建完毕后,创建任务,并开启线程执行


l 定义一个destroy方法,当ApiService被销毁前,结束线程任务


————————————————
版权声明:本文为CSDN博主「Mrherny」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Mrherny/article/details/80392012

原文地址:https://www.cnblogs.com/zhaojiu/p/14977153.html