RestTemplate:
是 Spring 提供的用于访问Rest服务的客户端, RestTemplate 提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
–注意RestTemplate只有初始化配置(也可以不配,有默认的),没有什么连接池


okhttp3:官方文档
OkHttp是一个高效的HTTP客户端:

  •   HTTP / 2支持允许对同一主机的所有请求共享一个套接字。

  •   连接池可减少请求延迟(如果HTTP / 2不可用)。

  •  透明的GZIP缩小了下载大小。

  •  响应缓存可以完全避免网络重复请求。

    当网络出现问题时,OkHttp会坚持不懈:它将从常见的连接问题中静默恢复。如果您的服务具有多个IP地址,则在第一次连接失败时,OkHttp将尝试使用备用地址。这对于IPv4 + IPv6和冗余数据中心中托管的服务是必需的。OkHttp支持现代TLS功能(TLS 1.3,ALPN,证书固定)。可以将其配置为回退以获得广泛的连接性。
    使用OkHttp很容易。它的请求/响应API具有流畅的构建器和不变性。它支持同步阻塞调用和带有回调的异步调用。

  • 添加依赖

<dependency>
       <groupId>com.squareup.okhttp3</groupId>
       <artifactId>okhttp</artifactId>
       <version>4.9.0</version>
</dependency>


application.properties设置相关配置参数

http:
  okhttp3:
    connect-timeout: 5
    read-timeout: 10
    write-timeout: 10
    # 连接池中整体的空闲连接的最大数量
    max-idle-connections: 200
    # 连接空闲时间最多为 300 秒
    keep-alive-duration: 300


RestTemplateConfig配置类

package cn.friends.config;

import cn.friends.config.properties.OkHttpProperties;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableConfigurationProperties(OkHttpProperties.class)
public class RestTemplateConfig {

    private OkHttpProperties okHttpProperties;


    /**
     * 声明 RestTemplate
     */
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate(httpRequestFactory());
        // 中文乱码,主要是 StringHttpMessageConverter的默认编码为ISO导致的
        List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
        for (HttpMessageConverter<?> converter : list) {
            if (converter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);
                break;
            }
        }
        // 可以增加拦截器
        MyRequestInterceptor myRequestInterceptor = new MyRequestInterceptor();
        restTemplate.getInterceptors().add(myRequestInterceptor);
        return restTemplate;
    }

    public ClientHttpRequestFactory httpRequestFactory() {
        return new OkHttp3ClientHttpRequestFactory(okHttpConfigClient());
    }

    public OkHttpClient okHttpConfigClient() {
        return new OkHttpClient().newBuilder()
                .connectionPool(pool())
                .connectTimeout(okHttpProperties.getConnectTimeout(), TimeUnit.SECONDS)
                .readTimeout(okHttpProperties.getReadTimeout(), TimeUnit.SECONDS)
                .writeTimeout(okHttpProperties.getWriteTimeout(), TimeUnit.SECONDS)
                .hostnameVerifier((hostname, session) -> true)
                // 设置代理
                // .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)))
                // 拦截器
                // .addInterceptor()
                .build();
    }

    public ConnectionPool pool() {
        return new ConnectionPool(okHttpProperties.getMaxIdleConnections(), okHttpProperties.getKeepAliveDuration(),
         TimeUnit.SECONDS);
    }

}


自定义拦截器

    /**
     * 自定义的拦截器
     *
     * 主要打印日志
     *
     */
    class MyRequestInterceptor implements ClientHttpRequestInterceptor {
 
        @Override
        public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body, 
        ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
            //记录请求开始时间
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            //执行请求
            ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, body);
            //执行完毕后,这里要创建备份,要不然会报io提前关闭的异常错误
            ClientHttpResponse responseCopy = new BufferingClientHttpResponseWrapper(response);
            //记录请求结束时间
            stopWatch.stop();
            //获取响应内容
            StringBuilder resBody = new StringBuilder();
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(responseCopy.getBody(),
                    Charset.forName("UTF-8")))) {
                String line = bufferedReader.readLine();
                while (line != null) {
                    resBody.append(line);
                    line = bufferedReader.readLine();
                }
            }
 
            //获取请求内容
            String reqBody = "";
            //这里可以自定义想打印什么方法或者请求的内容,如下只打印post请求的日志,因为这时候才会带上body
            if (httpRequest.getHeaders().getContentType() != null && "POST".equals(httpRequest.getMethod().name())) {
                String requestBody = new String(body, Charset.forName("UTF-8"));
//                String contentType = httpRequest.getHeaders().getContentType().toString();
                //这里可以对contentType做一些判断,针对其格式化请求体的内容,
                如"application/x-www-form-urlencoded"格式会附带一些boundary(分割线)的内容
                reqBody = requestBody;
 
            }
            
            //打印日志的格式可以自定义
            log.info(JSON.toJSONString(RestLog.builder().costTime(stopWatch.getLastTaskTimeMillis())
            .headers(httpRequest.getHeaders()).method(httpRequest.getMethodValue())
                    .reqUrl(httpRequest.getURI().toString()).reqBody(reqBody).resBody(resBody.toString())
                    .resStatus(responseCopy.getRawStatusCode()).build()));
 
            return responseCopy;
        }
    }
 
 
    /**
     * 响应内容备份
     */
    final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
 
        private final ClientHttpResponse response;
 
        private byte[] body;
 
 
        BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
            this.response = response;
        }
 
 
        @Override
        public HttpStatus getStatusCode() throws IOException {
            return this.response.getStatusCode();
        }
 
        @Override
        public int getRawStatusCode() throws IOException {
            return this.response.getRawStatusCode();
        }
 
        @Override
        public String getStatusText() throws IOException {
            return this.response.getStatusText();
        }
 
        @Override
        public HttpHeaders getHeaders() {
            return this.response.getHeaders();
        }
 
        @Override
        public InputStream getBody() throws IOException {
            if (this.body == null) {
                this.body = StreamUtils.copyToByteArray(this.response.getBody());
            }
            return new ByteArrayInputStream(this.body);
        }
 
        @Override
        public void close() {
            this.response.close();
        }
    }
 
    /**
     * 定义日志的字段
     */
    @Data
    @Builder
    private static class RestLog {
        private String reqUrl;
        private String method;
        private HttpHeaders headers;
        private String reqBody;
        private String resBody;
        private long costTime;
        private int resStatus;
    }


使用效果

@SpringBootTest
class SpringbootRestTemplateApplicationTests {
 
    @Resource
    RestTemplate restTemplate;
 
    @Test
    void contextLoads() {
        String requestUrl = "https://api.uomg.com/api/icp";
        //构建json请求参数
        JSONObject params = new JSONObject();
        params.put("domain", "www.baidu.com");
        //请求头声明请求格式为json
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        //创建请求实体
        HttpEntity<String> entity = new HttpEntity<>(params.toString(), headers);
        //执行post请求,并响应返回字符串
        String resText = restTemplate.postForObject(requestUrl, entity, String.class);
        System.out.println(resText);
    }
 
}
o.l.s.f.config.RestTemplateConfig        : 
{"costTime":1517,"headers":{"Accept":["text/plain, */*"],"Content-Type":["application/json"],"Content-Length":["26"]},
"method":"POST","reqBody":"{\"domain\":\" 
"resBody":"{\"code\":200901,\"msg\":\"查询域名不能为空\"}","resStatus":200}
{"code":200901,"msg":"查询域名不能为空"}


转自:https://blog.csdn.net/lrb0677/article/details/125510456