使用 Spring 配置 RestTemplate 结合 HttpClient 访问 Rest 接口

RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高客户端的编写效率。

调用 RestTemplate 的默认构造函数,RestTemplate 对象在底层通过使用 java.net 包下的实现创建 HTTP 请求,可以通过使用 ClientHttpRequestFactory 指定不同的 HTTP 请求方式。

ClientHttpRequestFactory 接口主要提供了两种实现方式:

  • 一种是 SimpleClientHttpRequestFactory,使用 J2SE 提供的方式(即 java.net 包提供的方式)创建底层的 Http 请求连接。
  • 一种方式是使用 HttpComponentsClientHttpRequestFactory 方式,底层使用 HttpClient 访问远程的 Http 服务,使用 HttpClient 可以配置连接池和证书等信息。

本文主要介绍的是使用 ClientHttpRequestFactory 接口的 HttpClient 实现方式来实现对 REST 接口的访问。

Spring 配置 RestTemplate

注意配置文件中部分被注释的部分,已经在代码中实现。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
     xmlns:task="http://www.springframework.org/schema/task" xmlns:cache="http://www.springframework.org/schema/cache"
     xmlns:p="http://www.springframework.org/schema/p"
     xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- http连接池 -->
    <!-- <bean id="pollingConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager"> -->
        <!-- 最大连接数 -->
        <!-- <property name="maxTotal" value="10" /> -->
        <!-- 同一个主机(IP)的最大并发连接数 -->
        <!-- <property name="defaultMaxPerRoute" value="5" />
    </bean> -->

    <!-- <bean id="httpClientBuilder" class="org.apache.http.impl.client.HttpClientBuilder" factory-method="create">
        <property name="connectionManager" ref="pollingConnectionManager" />
    </bean> -->

    <!-- <bean id="httpClient" factory-bean="httpClientBuilder" factory-method="build" /> -->

    <bean id="httpClient" class="com.liuqianfei.utils.HttpClientUtils" factory-method="acceptsUntrustedCertsHttpClient"/>

    <!-- -->
    <bean id="clientHttpRequestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
        <constructor-arg ref="httpClient" />
        <property name="connectTimeout" value="30000" />
        <property name="readTimeout" value="30000" />
    </bean>

    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <constructor-arg ref="clientHttpRequestFactory" />
        <!--
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
                <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
                <bean class="org.springframework.http.converter.FormHttpMessageConverter" />
                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
                <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter" />
            </list>
        </property>
         -->
    </bean>

</beans>

实现 HttpClientUtils

org.springframework.http.client.HttpComponentsClientHttpRequestFactory 对象的构造方法需要接受一个实现访问规则的 HttpClient 实例。

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * HttpClient工具,对https访问信任,不需要证书;<br>
 * 设置http连接池的最大连接数,和同一个主机(IP)的最大并发连接数。
 * @author liuqianfei
 *
 */
public class HttpClientUtils
{
    public static CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException
    {
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

        // setup a Trust Strategy that allows all certificates.
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy()
        {
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
            {
                return true;
            }
        }).build();

        httpClientBuilder.setSslcontext(sslContext);

        // don't check Hostnames, either.
        // -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if
        // you don't want to weaken
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

        // here's the special part:
        // -- need to create an SSL Socket Factory, to use our weakened "trust
        // strategy";
        // -- and create a Registry, to register it.
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory).build();

        // now, we create connection-manager using our Registry.
        // -- allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        httpClientBuilder.setConnectionManager(connMgr);

        // finally, build the HttpClient;
        // -- done!
        CloseableHttpClient client = httpClientBuilder.build();
        return client;
    }
}

在控制器中使用并访问 REST 接口

import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.support.WebApplicationContextUtils;

RestTemplate restTemplate = (RestTemplate) WebApplicationContextUtils.getWebApplicationContext(getRequest().getServletContext()).getBean("restTemplate");
String result = restTemplate.getForObject("https://192.168.1.10/lqf/business/getAllPolice", String.class);

RestTemplategetForObject 完成 get 请求、postForObject 完成 post 请求、put 对应的完成 put 请求、delete 完成 delete 请求;还有 execute 可以执行任何请求的方法,需要你设置 RequestMethod 来指定当前请求类型。

RestTemplate.getForObject(String url, Class responseType, String... urlVariables);

参数 url 是 http 请求的地址,参数 Class 是请求响应返回后的数据的类型,最后一个参数是请求中需要设置的参数。

template.getForObject(url + "get/{id}.do", String.class, id);

如上面的参数是 {id},返回的是一个 String 类型,设置的参数是 id。最后执行该方法会返回一个 String 类型的结果。

如果觉得这对你有用,请随意赞赏,给与作者支持
评论 0
最新评论