SpringBoot案例 调用第三方接口传输数据

一、前言

最近再写调用三方接口传输数据的项目,这篇博客记录项目完成的过程,方便后续再碰到类似的项目可以快速上手

项目结构:

二、编码

这里主要介绍HttpClient发送POST请求工具类和定时器的使用,mvc三层架构编码不做探究

pom.xml

xml 复制代码
<dependencies>
   <!--web启动依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- httpclient -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    <!--fastjson-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.79</version>
    </dependency>
    <!--swagger2-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.3.1</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.14</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!--测试单元-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

application-dev.yml

yml 复制代码
#####端口配置#####
server:
  port: 9991

#####数据源配置#####
spring:
  datasource:
    username: dev
    password: dev1234
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

#####mybatis配置#####
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.jzj.pojo
  configuration:
    map-underscore-to-camel-case: true

#####配置日志文件#####
logging:
  config: classpath:logback.xml
  #设置日志级别的节点
  level:
    com:
      jzj: debug

Constast

java 复制代码
package com.jzj.common;

public class Constast {
    /**
     * 请求头信息
     */
    public static final String CONTENT_TYPE = "application/json;charset=UTF-8";

    /**
     * 返回状态值
     */
    public static final Integer OK = 200;

    public static  final String YK_URL = "三方接口地址";
}

utils

java 复制代码
package com.jzj.utils;

import com.jzj.common.Constast;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
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.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * Apache HttpClient发送POST请求工具类
 *
 * @author 黎明
 * @version 1.0
 * @date 2023/8/14 14:18
 */
public class HttpUtils {

    /**
     * 发送post请求
     *
     * @param url       请求url
     * @param jsonParam 请求参数
     * @return 响应数据
     */
    public static String doPostJson(String url, String jsonParam) {
        // 创建一个HttpPost对象,并指定URL
        HttpPost httpPost = new HttpPost(url);
        // 声明一个CloseableHttpResponse对象来接收请求的响应
        CloseableHttpResponse response = null;
        // 创建一个CloseableHttpClient对象。wrapClient方法是自定义的方法,用于构建和配置HttpClient对象
        CloseableHttpClient httpClient = wrapClient(url);
        try {
            // 通过重新赋值的方式为HttpPost对象设置URL
            httpPost = new HttpPost(url);
            // 设置请求头的内容类型。Constast.CONTENT_TYPE表示请求的数据类型
            httpPost.setHeader("Content-type", Constast.CONTENT_TYPE);
            // 创建一个StringEntity对象,用于封装JSON参数。
            StringEntity entity = new StringEntity(jsonParam, "UTF-8");
            // 将实体的内容编码设置为与请求头的内容类型相同
            entity.setContentEncoding(new BasicHeader("Content-type", Constast.CONTENT_TYPE));
            // 将StringEntity对象设置为HttpPost请求的实体
            httpPost.setEntity(entity);
            // 执行HttpPost请求,并将响应赋值给response对象
            response = httpClient.execute(httpPost);
            // 判断响应的状态码是否等于200
            if (response.getStatusLine().getStatusCode() == Constast.OK) {
                // 将响应实体转换为字符串并返回。EntityUtils.toString方法用于读取响应实体的内容。
                return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            throw new RuntimeException("[发送POST请求错误:]" + e.getMessage());
        } finally {
            // 释放连接、关闭响应和关闭HttpClient对象
            try {
                httpPost.releaseConnection();
                response.close();
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 根据URL的协议来配置HttpClient对象
     * @param url url地址
     * @return CloseableHttpClient
     */
    private static CloseableHttpClient wrapClient(String url) {
        // 使用HttpClientBuilder类创建一个默认的HttpClient对象
        CloseableHttpClient client = HttpClientBuilder.create().build();
        if (url.startsWith("https")) { // 检查URL是否以"https"开头,以确定是否需要使用HTTPS协议
            // 如果URL以"https"开头,调用方法获取配置了HTTPS支持的CloseableHttpClient对象
            client = getCloseableHttpsClients();
        }
        return client;
    }

    /**
     * 创建一个支持HTTPS的CloseableHttpClient对象
     * @return CloseableHttpClient
     */
    private static CloseableHttpClient getCloseableHttpsClients() {
        // 采用绕过验证的方式处理https请求
        SSLClient ssl = new SSLClient();
        SSLContext sslcontext = ssl.createIgnoreVerifySSL();
        // 设置协议http和https对应的处理socket链接工厂的对象
        org.apache.http.config.Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
                .<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", new SSLConnectionSocketFactory(sslcontext)).build();
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        HttpClients.custom().setConnectionManager(connManager);
        // 创建自定义的httpsclient对象
        CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
        return client;
    }
}
java 复制代码
package com.jzj.utils;

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.cert.CertificateException;

/**
 * 用于创建一个支持绕过HTTPS验证的SSLContext对象
 *
 * @author 黎明
 * @version 1.0
 * @date 2023/8/14 14:35
 */
public class SSLClient {

    // 使用@SuppressWarnings注解来抑制未使用的警告
    @SuppressWarnings("unused")
    public SSLContext createIgnoreVerifySSL() {
        // 创建套接字对象
        SSLContext sslContext = null;
        try {
            // 指定TLS版本
            sslContext = SSLContext.getInstance("TLSv1.2");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("[创建套接字失败:] " + e.getMessage());
        }
        // 实现X509TrustManager接口,用于绕过验证
        X509TrustManager trustManager = new X509TrustManager() {
            // 该方法用于验证客户端证书
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            // 该方法用于验证服务器证书
            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            // 该方法返回受信任的颁发机构(证书颁发机构)数组。在这里,返回null表示不对颁发机构进行限制
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        try {
            // 初始化sslContext对象
            sslContext.init(null, new TrustManager[]{trustManager}, null);
        } catch (KeyManagementException e) {
            throw new RuntimeException("[初始化套接字失败:] " + e.getMessage());
        }
        return sslContext;
    }
}

scheduled

java 复制代码
package com.jzj.scheduled;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jzj.common.Constast;
import com.jzj.pojo.TrsfToYk;
import com.jzj.pojo.TrsfToYkLog;
import com.jzj.service.SfSfmxService;
import com.jzj.service.TrsfToYkLogService;
import com.jzj.service.TrsfToYkService;
import com.jzj.utils.HttpUtils;
import com.jzj.vo.YkResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 定时器任务
 *
 * @author 黎明
 * @version 1.0
 * @date 2023/8/15 15:03
 */
@Component
@Slf4j
public class TrsfToYkScheduled {
    // 注入trsfToYkService
    @Autowired
    private TrsfToYkService trsfToYkService;
    // 注入trsfToYkLogService
    @Autowired
    private TrsfToYkLogService trsfToYkLogService;
    // 注入SfSfmxService
    @Autowired
    private SfSfmxService sfSfmxService;

    /**
     * 审方信息下传英克
     */
    @Scheduled(cron = "*/10 * * * * *")
    public void toYk() {
        // 根据视图查询所有bs=0审方信息
        List<TrsfToYk> sfAllInfo = trsfToYkService.findAll();
        if (sfAllInfo.size() != 0) { // 判断是否有数据
            ObjectMapper mapper = new ObjectMapper();
            String requestData = null;
            try {
                requestData = mapper.writeValueAsString(sfAllInfo);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            log.info("发送的数据是:{}", requestData);
            String responseData = HttpUtils.doPostJson(Constast.YK_URL, requestData);
            log.info("响应的数据是:{}", responseData);
            JSONObject responseJson = JSONObject.parseObject(responseData);
            YkResultVo ykResultVo = responseJson.toJavaObject(YkResultVo.class);
            // 判断响应状态是否为200
            if (ykResultVo.getStatus().equals("200") && ykResultVo.getStatus() != null) {
                // 记录日志
                ykResultVo.getData().stream().forEach(v -> {
                    TrsfToYkLog trsfToYkLog = new TrsfToYkLog();
                    trsfToYkLog.setAuditId(v.getAuditId());
                    trsfToYkLog.setStatus(v.getStatus());
                    trsfToYkLogService.insertLog(trsfToYkLog);
                });

                // 更新审方明细表bs字段
                ykResultVo.getData().stream().filter(v -> v.getStatus().equals("200")).forEach(v -> {
                    long aid = Long.parseLong(v.getAuditId());
                    sfSfmxService.renewalBs(aid);
                });
            }
        }
    }
}

三、总结

该定时任务每10秒执行一次,将满足条件的审方信息发送到三方系统,并根据返回的结果进行相应的日志记录和数据更新操作。再调用三方接口时,使用的是封装好了的工具类将post请求发送给三方接口,并对https安全传输协议做了跳过操作。

相关推荐
Dola_Pan3 小时前
Linux文件IO(二)-文件操作使用详解
java·linux·服务器
wang_book3 小时前
Gitlab学习(007 gitlab项目操作)
java·运维·git·学习·spring·gitlab
蜗牛^^O^4 小时前
Docker和K8S
java·docker·kubernetes
从心归零4 小时前
sshj使用代理连接服务器
java·服务器·sshj
IT毕设梦工厂5 小时前
计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
Ylucius6 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
凡人的AI工具箱6 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
是店小二呀6 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
七夜zippoe6 小时前
分布式系统实战经验
java·分布式
canonical_entropy6 小时前
金蝶云苍穹的Extension与Nop平台的Delta的区别
后端·低代码·架构