从Apache HttpClient类库,说一说springboot应用程序中的AutoConfiguration的封装

一、背景

在使用httpclient框架请求http接口的时候,我们往往会需要自定义配置httpclient,而非直接使用。

		<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>

在这里插入图片描述

本文仅以自定义httpclient为例,目的是看在springboot应用程序中,如何写一个AutoConfiguration配置类。

涉及到的类有以下六个:

  • HttpClientAutoConfiguration
  • HttpClientProperties
  • ApacheHttpClientConnectionManagerFactory
  • ApacheHttpClientFactory
  • DefaultApacheHttpClientConnectionManagerFactory
  • DefaultApacheHttpClientFactory

在这里插入图片描述
在这里插入图片描述

二、写一个AutoConfiguration

1、HttpClientAutoConfiguration

HttpClientAutoConfiguration类提供了一个自动化配置HTTP客户端的解决方案,使得开发者可以方便地在Spring Boot应用中使用Apache HttpClient库。通过配置文件和自动配置,它创建了HTTP连接管理器和HTTP客户端,并且安排了定时任务来维护这些连接。


import com.google.common.util.concurrent.ThreadFactoryBuilder;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


@Configuration
// 使用配置类HttpClientProperties
@EnableConfigurationProperties(value = {HttpClientProperties.class})
public class HttpClientAutoConfiguration {
    private final ScheduledExecutorService scheduledService;
    private CloseableHttpClient httpClient;

    @Autowired
    public HttpClientAutoConfiguration() {
        // 定义一个单线程池
        this.scheduledService = new ScheduledThreadPoolExecutor(
                1,
                new ThreadFactoryBuilder().setNameFormat("http-client-manager").build(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

    @Bean
    @ConditionalOnMissingBean
    public ApacheHttpClientConnectionManagerFactory connManFactory() {
        return new DefaultApacheHttpClientConnectionManagerFactory();
    }

    @Bean
    @ConditionalOnMissingBean
    public HttpClientBuilder apacheHttpClientBuilder() {
        return HttpClientBuilder.create();
    }

    @Bean
    @ConditionalOnMissingBean
    public ApacheHttpClientFactory apacheHttpClientFactory(
            HttpClientBuilder builder) {
        return new DefaultApacheHttpClientFactory(builder);
    }

    /**
     * Connection manager
     */
    @Bean
    @ConditionalOnMissingBean
    public HttpClientConnectionManager connectionManager(
            ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
            HttpClientProperties httpClientProperties) {
        final HttpClientConnectionManager connectionManager = connectionManagerFactory
                .newConnectionManager(httpClientProperties.getDisableSslValidation(),
                        httpClientProperties.getMaxConnections(),
                        httpClientProperties.getMaxConnectionsPerRoute(),
                        httpClientProperties.getTimeToLive(),
                        httpClientProperties.getTimeToLiveUnit());


        // 定时关闭过期的HTTP连接
        this.scheduledService.scheduleWithFixedDelay(connectionManager::closeExpiredConnections, 1000,
                httpClientProperties.getConnectionTimerRepeat(), TimeUnit.MILLISECONDS);

        return connectionManager;
    }

    /**
     * Http client
     */
    @Bean
    @ConditionalOnMissingBean
    public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
                                          HttpClientConnectionManager httpClientConnectionManager,
                                          HttpClientProperties httpClientProperties) {
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(httpClientProperties.getConnectionTimeout())
                .setRedirectsEnabled(httpClientProperties.getFollowRedirects())
                .build();

        this.httpClient = httpClientFactory
                .createBuilder()
                .setDefaultRequestConfig(defaultRequestConfig)
                .setConnectionManager(httpClientConnectionManager)
                .build();
        return this.httpClient;
    }

    /**
     * Destroy
     */
    @PreDestroy
    // @PreDestroy注解的destroy()方法,用于在应用关闭时执行清理工作,关闭线程池scheduledService和httpClient
    public void destroy() throws Exception {
        this.scheduledService.shutdown();
        if (this.httpClient != null) {
            this.httpClient.close();
        }
    }
}

2、HttpClientProperties配置文件

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.concurrent.TimeUnit;

@Data
@ConfigurationProperties(prefix = "httpclient")
public class HttpClientProperties {

    /**
     * Value for disabling SSL validation.
     */
    private Boolean disableSslValidation = false;

    /**
     * Value for max number od connections.
     */
    private Integer maxConnections = 200;

    /**
     * Value for max number od connections per route.
     */
    private Integer maxConnectionsPerRoute = 50;

    /**
     * Value for time to live.
     */
    private Long timeToLive = 900L;

    /**
     * Time to live unit.
     */
    private TimeUnit timeToLiveUnit = TimeUnit.SECONDS;

    /**
     * Value for following redirects.
     */
    private Boolean followRedirects = true;

    /**
     * Value for connection timeout.
     */
    private Integer connectionTimeout = 2000;

    /**
     * Value for connection timer repeat.
     */
    private Integer connectionTimerRepeat = 3000;
}

3、定义两个工厂接口

  • ApacheHttpClientConnectionManagerFactory
import org.apache.http.conn.HttpClientConnectionManager;

import java.util.concurrent.TimeUnit;

public interface ApacheHttpClientConnectionManagerFactory {
    String HTTP_SCHEME = "http";

    /**
     * Scheme for HTTPS based communication.
     */
    String HTTPS_SCHEME = "https";

    /**
     * Creates a new {@link HttpClientConnectionManager}.
     *
     * @param disableSslValidation   If true, SSL validation will be disabled.
     * @param maxTotalConnections    The total number of connections.
     * @param maxConnectionsPerRoute The total number of connections per route.
     * @param timeToLive             The time a connection is allowed to exist.
     * @param timeUnit               The time unit for the time-to-live value.
     *                               manager.
     * @return A new {@link HttpClientConnectionManager}.
     */
    HttpClientConnectionManager newConnectionManager(boolean disableSslValidation,
                                                     int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive,
                                                     TimeUnit timeUnit);
}

  • ApacheHttpClientFactory
import org.apache.http.impl.client.HttpClientBuilder;

public interface ApacheHttpClientFactory {
    /**
     * Create builder
     *
     * @return HttpClientBuilder
     */
    HttpClientBuilder createBuilder();

4、工厂接口的实现

  • DefaultApacheHttpClientConnectionManagerFactory
import lombok.extern.slf4j.Slf4j;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
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.conn.PoolingHttpClientConnectionManager;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

@Slf4j
public class DefaultApacheHttpClientConnectionManagerFactory implements ApacheHttpClientConnectionManagerFactory {

    @Override
    public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation,
                                                            int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive,
                                                            TimeUnit timeUnit) {
        RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create()
                .register(HTTP_SCHEME, PlainConnectionSocketFactory.INSTANCE);

        if (disableSslValidation) {
            try {
                final SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null,
                        new TrustManager[]{new DisabledValidationTrustManager()},
                        new SecureRandom());
                registryBuilder.register(HTTPS_SCHEME, new SSLConnectionSocketFactory(
                        sslContext, NoopHostnameVerifier.INSTANCE));
            } catch (NoSuchAlgorithmException | KeyManagementException e) {
                log.warn("Error creating SSLContext", e);
            }
        } else {
            registryBuilder.register("https", SSLConnectionSocketFactory.getSocketFactory());
        }

        final Registry<ConnectionSocketFactory> registry = registryBuilder.build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
                registry, null, null, null, timeToLive, timeUnit);
        connectionManager.setMaxTotal(maxTotalConnections);
        connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute);

        return connectionManager;
    }

    static class DisabledValidationTrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }
}

  • DefaultApacheHttpClientFactory
import org.apache.http.impl.client.HttpClientBuilder;

public class DefaultApacheHttpClientFactory implements ApacheHttpClientFactory {

    private final HttpClientBuilder builder;

    public DefaultApacheHttpClientFactory(HttpClientBuilder builder) {
        this.builder = builder;
    }

    @Override
    public HttpClientBuilder createBuilder() {
        return this.builder.disableContentCompression().disableCookieManagement()
                .useSystemProperties();
    }
}

三、使用自定义httpclient对象

protected final CloseableHttpClient httpClient;


protected AbstractChannel(CloseableHttpClient httpClient) {
        this.httpClient = httpClient;
    }


protected String postJson(String url, String body) throws IOException {
        RequestConfig requestConfig = RequestConfig.custom()
                // 200 ms
                .setConnectionRequestTimeout(1000)
                // 1000ms
                .setSocketTimeout(1000)
                .build();

        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");

        // Execute
        StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        CloseableHttpResponse response = this.httpClient.execute(httpPost);

        // Response data.
        String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
        int statusCode = response.getStatusLine().getStatusCode();
        response.close();

        // Http code
        if (statusCode != 200) {
            throw new RuntimeException(String.format("http post failed! status=%d %s", statusCode, responseBody));
        }

        return responseBody;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/609273.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SPSS之主成分分析

SPSS中主成分分析功能在【分析】--【降维】--【因子分析】中完成&#xff08;在SPSS软件中&#xff0c;主成分分析与因子分析均在【因子分析】模块中完成&#xff09;。 求解主成分通常从分析原始变量的协方差矩阵或相关矩阵着手。 &#xff08;1&#xff09;当变量取值的度量…

20232820 2023-2024-2 《网络攻防实践》实践九报告

20232820 2023-2024-2 《网络攻防实践》实践九报告 1.实践内容 本次实践的对象是一个名为pwn1的linux可执行文件。 该程序正常执行流程是&#xff1a;main调用foo函数,foo函数会简单回显任何用户输入的字符串。 该程序同时包含另一个代码片段&#xff0c;getShell&#xff…

从开发角度理解漏洞成因(02)

文章目录 文件上传类需求文件上传漏洞 文件下载类需求文件下载漏洞 扩展 留言板类&#xff08;XSS漏洞&#xff09;需求XSS漏洞 登录类需求cookie伪造漏洞万能密码登录 持续更新中… 文章中代码资源已上传资源&#xff0c;如需要打包好的请点击PHP开发漏洞环境&#xff08;SQL注…

当导师和学生陷入「隐形冲突」

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

AI写的论文AI疑似度太高怎么办?教你一招解决

随着 AI 技术迅猛发展&#xff0c;各种AI辅助论文写作的工具层出不穷&#xff01; 为了防止有人利用AI工具进行论文代写&#xff0c;在最新的学位法中已经明确规定“已经获得学位者&#xff0c;在获得该学位过程中如有人工智能代写等学术不端行为&#xff0c;经学位评定委员会…

易基因:Nature子刊:ChIP-seq等揭示c-di-AMP与DasR互作以调控细菌生长、发育和抗生素合成|项目文章

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 c-di-AMP是一种在细菌信号中普遍存在且至关重要的核苷酸第二信使&#xff0c;对于大多数c-di-AMP合成生物体来说&#xff0c;c-di-AMP稳态及其信号转导的分子机制非常值得关注。 2024年…

智慧仓储可视化大屏,以最直观的形式展示海量数据。

智慧仓储可视化大屏是一种通过数据可视化技术&#xff0c;将仓储管理系统中的海量数据以图表、地图、仪表盘等形式直观展示在大屏上的解决方案。它可以帮助仓储管理人员更清晰地了解仓库的运营情况&#xff0c;从而做出更明智的决策。 智慧仓储可视化大屏通常包括以下功能和特点…

护眼灯有没有护眼的效果?六大技巧教你选到护眼效果好的护眼台灯

随着孩子学习压力增大&#xff0c;护眼灯的重要性日益凸显。那么&#xff0c;护眼灯有没有护眼的效果&#xff1f;答案是肯定的&#xff0c;但关键在于如何挑选。本文将分享六大选购技巧&#xff0c;帮助大家挑选到护眼效果卓越的台灯&#xff0c;确保孩子在明亮而舒适的光线下…

论文AI疑似度太高怎么办?教你一招解决AIGC降重问题

随着 AI 技术迅猛发展&#xff0c;各种AI辅助论文写作的工具层出不穷&#xff01; 为了防止有人利用AI工具进行论文代写&#xff0c;在最新的学位法中已经明确规定“已经获得学位者&#xff0c;在获得该学位过程中如有人工智能代写等学术不端行为&#xff0c;经学位评定委员会…

苹果15能用哪些充电宝?充电宝什么牌子好?好用充电宝排名

随着移动设备的普及和功能的不断强大&#xff0c;我们对于充电宝的需求也越来越高。尤其是对于苹果15用户来说&#xff0c;选择一款兼容性好、性能稳定的充电宝显得尤为重要。在市面上众多充电宝品牌中&#xff0c;如何选择适合苹果15的充电宝&#xff1f;究竟哪个牌子的充电宝…

【密评】 | 商用密码应用安全性评估从业人员考核题库(7/58)

量子密钥分发&#xff08;QKD&#xff09;技术采用&#xff08;&#xff09;作为信息载体&#xff0c;经由量子通道在合法的用户之间传送密钥。 A. 数据 B. 电流 C. 量子态 D. 文本 置换&#xff08;permutation&#xff09;密码是把明文中的各字符&#xff08;&#xff09;得…

tag-字符串:最长公共前缀

题目 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 示例 题解一 class Solution:def longestCommonPrefix(self, strs: List[str]) -> str:# 按照字典顺序找到strs中最大的字符串和最小的字符串str0 min(strs)st…

深入了解二叉搜索树:原理、操作与应用

文章目录 二叉搜索树二叉搜索树的操作1.查找操作2.插入操作3.查找最大值或者最小值4.删除操作5.前序中序后序遍历 总结 二叉搜索树 形如上图的二叉树就是二叉搜索树&#xff0c;接下来我们来具体阐述一下什么是二叉搜索树。 二叉搜索树的概念&#xff1a;满足左子树的值小于根…

winform图书销售管理系统+mysql

winform图书销售管理系统mysql数据库说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 功能模块&#xff1a; 管理员:ttt 123 登陆可以操作我的 个人信息 修改密码 用户信息 添加删除用户 图书 添加删除图书信息 购物车 购买订单信息 充值 退出账户 …

网络安全之弱口令与命令爆破(下篇)(技术进阶)

目录 一&#xff0c;什么是弱口令&#xff1f; 二&#xff0c;为什么会产生弱口令呢&#xff1f; 三&#xff0c;字典的生成 四&#xff0c;九头蛇&#xff08;hydra&#xff09;弱口令爆破工具 1&#xff0c;破解ssh登录密码 2&#xff0c;破解windows登录密码 3&#xf…

Cesium的使用和特点

Cesium 是一款开源的 JavaScript 库&#xff0c;用于在 Web 上可视化地理空间数据。它广泛用于创建 3D 地球、地图和其他地理空间应用程序。Cesium 具有以下特点使其成为地理空间开发的流行选择。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交…

华为OD机试 - 手机App防沉迷系统(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

git合并分支

1、vscode安装插件Git Graph 2、点击右下角分支&#xff0c;比如要把dev分支合并到其他分支 首先dev分支的全部提交了&#xff0c;然后切换到其他分支 3、选择被合并的分支 点提交就好了

中医课堂丨名医面对面,金保方教授专场健康科普交流圆满举行

5月8日下午&#xff0c;李良济特邀金保方教授&#xff0c;在苏州太湖国际高尔夫俱乐部&#xff0c;以“生殖健康漫谈”为主题开展专场科普交流活动&#xff0c;参与的嘉宾表示受益匪浅&#xff0c;反响强烈。 本次活动主要包含了专家讲座、专家答疑义诊&#xff0c;现在就让我…

Java基础编程(高级部分)

1. 类变量和类方法 1.1 什么是类变量 类变量也叫静态变量/静态属性&#xff0c;是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值同样任何一个该类的对象去修改它时,修改的也是同一个变量。 1.2 定义类变量 1.3 访问类变量 类名.类变量名 或者 对…
最新文章