java中http调用组件深入详解

目录

一、前言

二、http调用概述

2.1 什么是http调用

2.1.1 http调用步骤

2.2 HTTP调用特点

2.3 HTTP调用应用场景

三、微服务场景下http调用概述

3.1 微服务开发中http调用场景

3.2 微服务组件中http的应用

四、常用的http调用组件

4.1 java中常用的http组件介绍

4.1.1 HttpClient

4.1.2 OkHttp

4.1.3 RestTemplate

4.2 RestClient介绍

五、http组件调用实践

5.1 前置准备

5.1.1 工程1提供两个测试接口

5.2 OkHttp使用

5.2.1 引入OkHttp依赖

5.2.2 创建OkHttp配置类

5.2.3 OkHttp工具类

5.2.4 提供两个测试接口

5.2.5 Okhttp使用优化

5.3 HttpClient使用

5.3.1 导入httpclient依赖

5.3.2 添加httpclient工具类

5.3.3 添加httpclient测试接口

5.3.4 测试调用效果

5.3.5 HttpClient配置优化

5.4 RestTemplate使用

5.4.1 添加依赖

5.4.2 添加初始化配置

5.4.3 测试GET接口

5.4.4 测试POST接口

5.5 RestClient使用

六、写在文末


一、前言

在java项目中,尤其是在微服务开发中,不同服务之间难免要进行通信,由于微服务属于不同的进程,在这种情况下,不同服务间通过http请求调用是很常见的,但是在开发过程中,选择哪种方式调用,选择什么样的sdk,或者说如何更高效的调用http接口等,很多同学并没有深入的研究过,本文将详细探讨一下。

二、http调用概述

2.1 什么是http调用

HTTP调用是指客户端(如浏览器、移动应用程序等)使用HTTP协议向服务器发出请求,并且服务器响应这些请求的过程。

在HTTP调用中,客户端发送HTTP请求,服务器接收并处理该请求,最后服务器返回HTTP响应给客户端。这种模式使得客户端能够与服务器进行通信,并获取所需的数据或服务。

2.1.1 http调用步骤

HTTP调用通常涉及以下几个基本步骤:

  • 客户端发起请求:客户端通过HTTP协议构建一个请求消息,包括请求方法(比如GET、POST)、请求头、请求体等信息。然后将请求消息发送给服务器。

  • 服务器处理请求:服务器接收到客户端发送的请求消息后,根据请求消息中的信息进行相应的处理,可能是查询数据库、生成动态内容、执行业务逻辑等。

  • 服务器返回响应:服务器处理完请求后,生成一个包含响应状态码、响应头和响应体的HTTP响应消息,并将其发送回客户端。

  • 客户端接收响应:客户端接收到服务器发送的HTTP响应消息后,根据响应消息中的信息进行相应的处理,可能是解析响应内容、展示数据、执行下一步操作等。

2.2 HTTP调用特点

对于HTTP调用来说,具有如下的特点

  • 简单易用:HTTP协议采用文本格式交换数据,易于人类理解和调试。HTTP调用的请求和响应消息都以明文形式传输,便于开发人员进行调试和排查问题。

  • 跨平台兼容性强:HTTP是一种跨平台的协议,可以在不同操作系统、不同编程语言的环境下使用。这使得HTTP调用能够在各种设备和系统之间进行通信。

  • 状态无关:HTTP协议是一种状态无关的协议,每个请求都是相互独立的,服务器不会保存客户端的状态信息。这样设计简化了服务器的管理,也使得系统更易于扩展和维护。

  • 支持多种请求方法:HTTP支持多种请求方法,如GET、POST、PUT、DELETE等,可以实现不同类型的操作。通过选择合适的请求方法,可以对资源进行查询、创建、更新、删除等操作。

  • 可扩展性强:HTTP协议支持头部字段自定义,允许扩展协议功能。开发人员可以利用HTTP头部字段来传递自定义信息,实现更复杂的交互逻辑。

  • 无连接:HTTP是一种无连接的协议,即每次请求和响应完成后就关闭连接。这样设计降低了服务器的负担,但也导致了每次请求都需要建立新的连接,可能会增加延迟。

2.3 HTTP调用应用场景

HTTP调用在各种应用场景下都有广泛的应用,主要包括以下几个方面:

  • Web开发:在Web开发中,HTTP调用被广泛应用于浏览器与服务器之间的通信。通过HTTP请求和响应,网页可以获取动态内容、发送表单数据、上传文件等操作。

  • API服务:许多应用程序使用HTTP调用来访问第三方API服务,以获取数据或执行特定操作。例如,社交媒体平台提供的API可以通过HTTP调用来实现用户身份验证、发布内容等功能。

  • 微服务架构:在微服务架构中,不同的服务之间通过HTTP调用进行通信,实现服务之间的解耦和协作。每个微服务都可以通过HTTP接口向其他微服务发送请求并获取响应。

  • 移动应用开发:移动应用通常需要与服务器进行数据交换,而HTTP调用是实现移动应用与服务器通信的主要方式。移动应用可以通过HTTP调用获取最新数据、同步用户信息等操作。

  • 云计算:在云计算环境下,不同的服务和资源可能分布在不同的服务器上,通过HTTP调用可以实现这些资源之间的通信和协作。云计算平台也提供了HTTP调用的API接口,供开发者管理和控制云资源。

  • 数据交换:HTTP调用也可以用于数据交换,如传输XML、JSON等格式的数据。应用程序可以通过HTTP调用将数据上传到服务器或从服务器下载数据,实现数据交换和同步。

总的来说,HTTP调用在现代互联网应用开发中扮演着重要的角色,为不同系统、服务和设备之间提供了一种标准化的通信方式,使得它们能够相互协作、交换数据,并提供各种功能和服务。

三、微服务场景下http调用概述

随着微服务开发模式盛行,为了方便开发人员更便捷的进行服务之间接口的调用,微服务框架做了更进一步的封装,比如springcloud系列的feign,即是对http调用的组件封装,而dubbo则是利用其他的协议,总的来说都是为了简化传统的http调用过程的繁琐的代码编写。

3.1 微服务开发中http调用场景

在微服务架构下,HTTP调用被广泛应用于各种场景,具体来说,主要包括以下几个方面:

  • 服务间通信

    • 微服务架构将一个应用程序拆分为多个独立的服务,每个服务都可以通过HTTP调用与其他服务进行通信。服务之间通过HTTP接口发送请求和获取响应,实现了服务之间的解耦和互操作。

  • 负载均衡

    • 由于微服务架构中服务数量较多,需要使用负载均衡器来均衡流量和请求。通过HTTP调用,负载均衡器可以将请求分发给不同的服务实例,以提高系统的性能和可伸缩性。

  • 弹性设计

    • 在微服务架构中,服务可能会出现故障或者需要进行水平扩展。通过HTTP调用,服务可以动态地注册、发现和调用其他服务,从而实现弹性设计,确保系统的可靠性和可用性。

  • 网关路由

    • 微服务架构通常会使用API网关来管理和路由所有的HTTP请求。API网关可以根据请求的路径、参数等信息将请求路由到相应的服务实例,同时还可以进行认证、安全检查等操作。

  • 分布式事务

    • 在微服务架构中,一个业务操作可能涉及多个不同的服务,需要保证这些服务的操作是一致和原子的。通过HTTP调用,可以使用分布式事务协调器来实现跨服务的分布式事务管理。

总结来说,在微服务架构下,HTTP调用作为一种轻量、灵活的通信方式,为不同服务提供了标准化的交互方式,使服务间能够相互通信、协作,实现复杂业务逻辑和功能。HTTP调用在微服务架构中具有重要的作用,已成为许多企业和组织构建分布式系统的首选通信方式。

3.2 微服务组件中http的应用

在微服务架构体系中,各个服务组件的协同,其底层通信机制离不开http的调用,具体来说,在使用HTTP调用微服务组件时,通常会涉及以下几个关键组件和方面:

  1. 服务提供者:服务提供者是暴露API接口的微服务实例,负责提供特定的功能或服务。服务提供者通过HTTP暴露RESTful风格的API接口,使其他服务可以通过HTTP调用来请求其提供的服务。

  2. 服务消费者:服务消费者是需要调用其他服务的微服务实例,它们通过HTTP发起请求,并获取服务提供者返回的响应数据。服务消费者根据需求向服务提供者发起HTTP调用,以获取所需的数据或执行相应的操作。

  3. 服务注册与发现:在微服务架构中,通常会有一个服务注册与发现组件用于管理所有微服务的注册和发现。服务提供者会将自己的信息注册到服务注册中心,而服务消费者可以通过服务发现机制查询并找到需要调用的服务实例。

  4. 负载均衡器:负载均衡器用于平衡不同服务实例之间的负载,确保流量被合理地分发到各个服务实例上。通过负载均衡器,可以提高系统的性能和可伸缩性,避免单个服务实例被过度请求。

  5. 断路器模式:为了防止故障的扩散,微服务架构中的HTTP调用通常会采用断路器模式。当某个服务出现故障或请求超时时,断路器会打开,阻止进一步的调用,并提供降级策略或错误处理机制。

  6. 安全认证与授权:在微服务架构中,由于服务数量众多,安全认证和授权变得尤为重要。通过HTTP调用,可以在请求头中传递认证信息,如JWT令牌、OAuth令牌等,以确保只有授权的服务能够调用受保护的服务。

以上组件和机制共同协作,实现了微服务架构中基于HTTP调用的服务之间的通信和协作。这些组件为微服务架构中的HTTP调用提供了必要的基础设施和支持,确保了系统的稳定性、可靠性和可扩展性。

四、常用的http调用组件

4.1 java中常用的http组件介绍

Java中常用的HTTP调用组件有很多,其中比较常见且使用广泛的包括:

  • Apache HttpClient

    • Apache HttpClient是Apache软件基金会提供的一个开源的HTTP客户端库,它提供了灵活和强大的功能,可以用来发送HTTP请求、处理响应等。

  • OkHttp

    • OkHttp是Square公司开发的一个高性能的HTTP客户端库,它支持HTTP/2 和 SPDY,使用简单且性能优秀。

  • Spring的RestTemplate

    • Spring框架提供了RestTemplate类来简化HTTP访问,它封装了大量的HTTP操作方法,使用方便。

  • HttpURLConnection:

    • Java标准库中自带的HttpURLConnection类也可以用来进行HTTP调用,虽然相对简单,但足以满足一些基本的需求。

4.1.1 HttpClient

HttpClient是Apache软件基金会提供的一个开源的HTTP客户端库,它是一个功能强大且灵活的工具,用于发送HTTP请求、处理响应等操作。HttpClient可以用于构建各种类型的HTTP客户端,从简单的GET请求到复杂的RESTful API调用都可以方便地实现。其主要特点如下:

  • 支持HTTP/1.1和HTTP/2:HttpClient支持HTTP/1.1和HTTP/2协议,可以根据需要选择合适的协议版本进行通信。

  • 灵活的请求和响应处理:HttpClient提供了丰富的API来构建HTTP请求,并能够灵活地处理响应数据,包括文本、二进制数据、JSON等格式。

  • 连接管理和连接池:HttpClient内置了连接管理器和连接池,能够有效地管理HTTP连接,并提供了连接重用、路由优化等功能,提高了性能和效率。

  • 支持HTTPS:HttpClient支持HTTPS协议,可以与安全的SSL/TLS加密连接进行通信,确保数据传输的安全性。

  • 拦截器和过滤器:HttpClient提供了拦截器和过滤器机制,可以在请求发送前或响应返回后添加自定义的逻辑处理,例如日志记录、身份验证等。

  • 配置选项丰富:HttpClient提供了丰富的配置选项,可以进行各种定制化的设置,如超时时间、代理设置、重试策略等。

4.1.2 OkHttp

OkHttp是Square公司开发的一个高性能的HTTP客户端库,用于在Android和Java应用中进行网络通信。它提供了简洁易用的API,支持HTTP/2和SPDY协议,并具有连接池、请求压缩、缓存等优秀的特性。其主要特点如下:

支持HTTP/2和SPDY

OkHttp支持现代的HTTP/2和SPDY协议,可以实现多路复用和头部压缩,提高网络传输效率。

简洁且易用的API

OkHttp提供了清晰简洁的API设计,使用起来非常方便,可以轻松构建各种类型的HTTP请求。

高性能

OkHttp采用了异步和非阻塞的设计,能够处理大量并发请求,并通过连接池和缓存机制提高性能。

自动重试机制

OkHttp内置了自动重试机制,当网络请求失败时可以根据配置自动进行重试,提高请求的成功率。

拦截器

OkHttp提供了拦截器机制,可以在网络请求的不同阶段添加自定义逻辑,如日志记录、身份验证等。

支持WebSocket

除了HTTP通信外,OkHttp还支持WebSocket协议,可以方便地实现实时通信功能。

可扩展性强

OkHttp支持插件式的拓展机制,可以通过插件来增加额外的功能或定制化需求。

总的来说,OkHttp是一个功能强大且性能优异的HTTP客户端库,在Android和Java应用中被广泛使用,对于需要进行网络通信的应用来说是一个很好的选择。

4.1.3 RestTemplate

RestTemplate是Spring框架提供的一个用于简化HTTP访问的工具类,它封装了大量的HTTP操作方法,使得在Java应用中进行RESTful风格的API调用变得更加简单和方便。

  • 支持多种HTTP请求方式

    • RestTemplate支持GET、POST、PUT、DELETE等多种HTTP请求方式,可以满足各种不同类型的API调用需求。

  • 内置消息转换器

    • RestTemplate内置了消息转换器,可以自动将HTTP请求和响应转换成Java对象,简化了数据的处理流程。

  • 支持异步请求

    • RestTemplate提供了异步请求的功能,可以发送异步的HTTP请求并处理返回结果,避免阻塞主线程。

  • 拦截器和过滤器

    • 类似于HttpClient,RestTemplate也支持拦截器和过滤器机制,可以在请求发送前或响应返回后添加自定义的逻辑处理。

  • 集成Spring生态系统

    • 作为Spring框架的一部分,RestTemplate可以与其他Spring组件无缝集成,如Spring Boot、Spring Cloud等,方便进行全栈开发。

  • 简化RESTful API调用

    • 由于RestTemplate封装了大量HTTP操作方法,因此使用它可以简化对RESTful API的调用,减少了代码重复性。

RestTemplate是一个功能丰富且易于使用的HTTP客户端库,在Spring应用中被广泛应用于RESTful API的调用和HTTP通信操作。

4.2 RestClient介绍

RestClient 是一个类似于 RestTemplate 的的同步接口调用工具。相比于 RestTemplate 采用的是 template 设计模式,RestClient 采用了 fluent API 风格,简单灵活,易于阅读和维护。

在Spring Boot 3.2版本中引入了RestClient,这是一个建立在WebClient之上的更高级抽象。RestClient通过提供更直观流畅的API并减少样板文件代码,进一步简化了HTTP请求的生成过程。它保留了WebClient的所有功能,同时提供了一个对开发人员更友好的界面。

五、http组件调用实践

下面通过案例来演示下http常用的几种组件如何使用。

5.1 前置准备

创建两个springboot工程,工程1提供2个用于测试的接口,然后再在工程2中编写接口,使用不同的http组件进行调用。

5.1.1 工程1提供两个测试接口

提供一个GET和POST接口

import com.congge.entity.DbUser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@RestController
@RequestMapping("/user")
public class DbUserController {

    @PostMapping("/add")
    public Map<String,Object> addUser(@RequestBody DbUser dbUser){
        if(StringUtils.isBlank(dbUser.getUserName())){
            throw new RuntimeException("用户名不能为空");
        }
        DbUser user = new DbUser();
        user.setId(1);
        user.setUserName(dbUser.getUserName());
        user.setAddress("杭州");
        Map<String,Object> resMap = new HashMap<>();
        resMap.put("code",200);
        resMap.put("msg","success");
        resMap.put("data",user);
        return resMap;
    }

    @GetMapping("/detail")
    public DbUser getById(@RequestParam Integer id){
        if(Objects.isNull(id)){
            throw new RuntimeException("id不能为空");
        }
        DbUser user = new DbUser();
        user.setId(1);
        user.setUserName("jerry");
        user.setAddress("杭州");
        return user;
    }

}

测试一下结果

5.2 OkHttp使用

参照如下步骤进行使用

5.2.1 引入OkHttp依赖

版本可以根据自身的需求进行选择

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>

5.2.2 创建OkHttp配置类

类似于其他的一些技术组件,在与springboot整合时,为了更好的控制该bean的使用,提供一个全局的配置bean,可以在bean的设置中优化相关的参数设置

import okhttp3.OkHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OkHttpConfig {

    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient();
    }
}

5.2.3 OkHttp工具类

提供一个OkHttp的工具类,后续在调用的地方直接注入该工具类即可使用

package com.congge.utils;

import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Iterator;
import java.util.Map;

@Component
public class OkHttpUtils {

    private static final Logger logger = LoggerFactory.getLogger(OkHttpUtils.class);

    @Autowired
    private OkHttpClient  okHttpClient;

    /**
     * @param url     请求的url
     * @param paramMap 请求的参数,在浏览器?后面的数据,没有可以传null
     * @return
     */
    public  String get(String url, Map<String, Object> paramMap) {
        paramCheck(url, paramMap);

        String resBody = "";
        StringBuffer sb = new StringBuffer(url);
        boolean firstFlag = true;
        Iterator iterator = paramMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry<String, String>) iterator.next();
            if (firstFlag) {
                sb.append("?" + entry.getKey() + "=" + entry.getValue());
                firstFlag = false;
            } else {
                sb.append("&" + entry.getKey() + "=" + entry.getValue());
            }
        }

        Request request = new Request.Builder()
                .url(sb.toString())
                .build();
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if(200 !=status){
                throw new RuntimeException("调用失败");
            }
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            logger.error("okhttp3 put error >> ex = {}", e.getMessage());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return resBody;
    }

    private void paramCheck(String url, Map<String, Object> paramMap) {
        if(StringUtils.isEmpty(url)){
            throw new RuntimeException("url不能为空");
        }
        if(null == paramMap || paramMap.isEmpty()){
            throw new RuntimeException("参数不能为空");
        }
    }

    /**
     * post请求
     * @param url
     * @param json
     * @return
     * @throws Exception
     */
    public  String post(String url, String json) throws Exception {
        RequestBody body = RequestBody.create(json, MediaType.get("application/json"));
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Response response = okHttpClient.newCall(request).execute();
        return response.body().string();
    }

    /**
     * post
     *
     * @param url    请求的url
     * @param params post form 提交的参数
     * @return
     */
    public String post(String url, Map<String, Object> params) {

        paramCheck(url, params);
        String responseBody = "";
        FormBody.Builder builder = new FormBody.Builder();
        //添加参数
        if (params != null && params.keySet().size() > 0) {
            for (String key : params.keySet()) {
                builder.add(key, params.get(key).toString());
            }
        }
        Request request = new Request.Builder()
                .url(url)
                .post(builder.build())
                .addHeader("Content-Type", "application/json")
                .build();
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if(200 !=status){
                throw new RuntimeException("调用失败");
            }
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            logger.error("okhttp3 post error >> ex = {}", e.getMessage());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
    }

    /**
     * @param url     请求的url
     * @param paramMap 请求的参数,在浏览器?后面的数据,没有可以传null
     * @return
     */
    public String getForHeader(String url, Map<String, Object> paramMap) {
        paramCheck(url,paramMap);
        String responseBody = "";
        StringBuffer sb = new StringBuffer(url);
        boolean firstFlag = true;
        Iterator iterator = paramMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry<String, String>) iterator.next();
            if (firstFlag) {
                sb.append("?" + entry.getKey() + "=" + entry.getValue());
                firstFlag = false;
            } else {
                sb.append("&" + entry.getKey() + "=" + entry.getValue());
            }
        }
        Request request = new Request.Builder()
                .addHeader("key", "value")
                .url(sb.toString())
                .build();
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if(200 !=status){
                throw new RuntimeException("调用失败");
            }
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            logger.error("okhttp3 put error >> ex = {}", e.getMessage());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
    }

    /**
     * Post请求发送JSON数据....{"name":"zhangsan","pwd":"123456"}
     * 参数一:请求Url
     * 参数二:请求的JSON
     * 参数三:请求回调
     */
    public String postJsonParams(String url, String jsonParams) {
        String responseBody = "";
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if(200 !=status){
                throw new RuntimeException("调用失败");
            }
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            logger.error("okhttp3 post error >> ex = {}", e.getMessage());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
    }

    /**
     * Post请求发送xml数据....
     * 参数一:请求Url
     * 参数二:请求的xmlString
     * 参数三:请求回调
     */
    public String postXmlParams(String url, String xml) {
        String responseBody = "";
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if(200 !=status){
                throw new RuntimeException("调用失败");
            }
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            logger.error("okhttp3 post error >> ex = {}", e.getMessage());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
    }
}

5.2.4 提供两个测试接口

使用上述的工具类,编写两个测试接口远程调用一下工程1中的两个接口

import com.alibaba.fastjson.JSONObject;
import com.congge.entity.DbUser;
import com.congge.utils.OkHttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@Slf4j
public class OkHttpController {

    @Autowired
    private OkHttpUtils okHttpUtils;

    //http://localhost:8088/remote/get
    @GetMapping("/remote/get")
    public Object getRemoteUser(Integer id){
        String url = "http://localhost:8087/user/detail";
        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("id",id);
        String s = okHttpUtils.get(url, paramMap);
        System.out.println(s);
        DbUser dbUser = JSONObject.parseObject(s, DbUser.class);
        return dbUser;
    }

    //http://localhost:8088/remote/post
    @GetMapping("/remote/post")
    public Object postRemoteUser() throws Exception{
        String url = "http://localhost:8087/user/add";
        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("userName","jerry");
        paramMap.put("address","杭州");
        String resStr = okHttpUtils.post(url, JSONObject.toJSONString(paramMap));
        System.out.println(resStr);
        return resStr;
    }

}

请求GET接口效果

请求post接口效果

5.2.5 Okhttp使用优化

在本案例中,我们通过配置类提供了Okhttp全局的bean,不难发现,尽管在调用时没有什么区别,但是为了更好的优化Okhttp配置bean的使用,可以在创建OkHttpClient过程中,添加更多的参数从而更好的管理和优化OkHttpClient,类似于我们在使用线程池的时候通常使用自定义的线程池,参考下面的示例;

在配置文件添加如下参数

okhttp:
  proxy:
    enable: true # 启用代理
    host: 127.0.0.1 #代理IP(默认为本机)
    port: 15777 #端口(默认使用本机springboot应用端口)
    connectTimeout: 50 #链接超时时间
    readTimeout: 50 # 读取超时时间
    writeTimeout: 10 # 写入超时时间
    connectionPool: # 连接池配置
      maxConnect: 10 # 连接池的最大空闲连接数
      keepAlive: 20 # 每个连接的最大请求数

OkHttpClient配置类改写如下

@Configuration(value = "okHttpConfig")
public class OkHttpConfig {

    @Value("${okhttp.proxy.enable}")
    private boolean enable;

    @Value("${okhttp.proxy.host}")
    private String proxyHost;

    @Value("${okhttp.proxy.port}")
    private int proxyPort;

    @Value("${okhttp.proxy.connectTimeout}")
    private int connectTimeout;

    @Value("${okhttp.proxy.readTimeout}")
    private int readTimeout;

    @Value("${okhttp.proxy.writeTimeout}")
    private int writeTimeout;

    /**
     * 连接池的最大空闲连接数
     */
    @Value("${okhttp.proxy.connectionPool.maxConnect}")
    private int maxConnect;

    /**
     * 每个连接的最大请求数
     */
    @Value("${okhttp.proxy.connectionPool.keepAlive}")
    private int keepAlive;

    @Bean
    public OkHttpClient okHttpClient() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .callTimeout(connectTimeout, TimeUnit.SECONDS)
                .connectTimeout(connectTimeout, TimeUnit.SECONDS) // 连接超时时间
                .readTimeout(readTimeout, TimeUnit.SECONDS) 		// 读取超时时间
                .writeTimeout(writeTimeout, TimeUnit.SECONDS) 		// 写入超时时间
                .retryOnConnectionFailure(false)			 // 是否自动重试连接失败的请求
                .connectionPool(new ConnectionPool(maxConnect, keepAlive, TimeUnit.MINUTES)); 	// 设置连接池;
        if (enable) {
            builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
        }
        return builder.build();
    }
}

5.3 HttpClient使用

HttpClient在日常的项目开发中也是高频使用的http客户端调用组件,参考下面的步骤进行使用

5.3.1 导入httpclient依赖

版本可以自行选择,与springboot版本匹配即可

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

5.3.2 添加httpclient工具类

工具类中分别提供了一个GET和POST请求的工具方法

package com.congge.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;

public class HttpClientUtil {

    /**
     * get请求
     * @param url
     * @param params
     * @return
     */
    public static String get(String url, Map<String,Object> params) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        String sendUrl = url;
        // 拼接参数
        if (Objects.nonNull(params) && params.size() > 0) {
            sendUrl = connectParams(url, params);
        }
        HttpGet httpGet = new HttpGet(sendUrl);
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
            HttpEntity httpEntity = response.getEntity();
            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) {
                return EntityUtils.toString(httpEntity);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                close(httpClient, response);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        throw new RuntimeException("调用GET请求失败!");
    }

    /**
     * post请求
     * @param url
     * @param params
     * @param paramBody
     * @return
     */
    public static String post(String url, Map<String,Object> params, Map<String,Object> paramBody) {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        String sendUrl = url;
        // 1.拼接参数
        if (Objects.nonNull(params) && params.size() > 0) {
            sendUrl = connectParams(url, params);
        }
        HttpPost httpPost = new HttpPost(sendUrl);
        httpPost.setHeader("Content-Type", "application/json;charset=utf8");

        //2.设置request-body
        String jsonBody = JSONObject.toJSONString(paramBody);
        StringEntity stringEntity = new StringEntity(jsonBody, "UTF-8");
        stringEntity.setContentEncoding("UTF-8");
        stringEntity.setContentType("application/json");
        httpPost.setEntity(stringEntity);

        CloseableHttpResponse response = null;
        try {

            // 2.设置request-body
//            ByteArrayEntity entity = new ByteArrayEntity(requestBody.getBytes(StandardCharsets.UTF_8));
//            entity.setContentType("application/json");
//            httpPost.setEntity(entity);

            response = httpClient.execute(httpPost);
            HttpEntity httpEntity = response.getEntity();
            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) {
                return EntityUtils.toString(httpEntity);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                close(httpClient, response);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        throw new RuntimeException("调用POST请求失败!");
    }

    private static String connectParams(String url, Map<String,Object> params) {
        StringBuffer buffer = new StringBuffer();
        buffer.append(url).append("?");
        params.forEach((x, y) -> buffer.append(x).append("=").append(y).append("&"));
        buffer.deleteCharAt(buffer.length() - 1);
        return buffer.toString();
    }

    public static void close(CloseableHttpClient httpClient, CloseableHttpResponse httpResponse) throws IOException{
        if (null != httpClient) {
            httpClient.close();
        }
        if (null != httpResponse) {
            httpResponse.close();
        }
    }
}

5.3.3 添加httpclient测试接口

添加两个用于测试的接口

@RestController
@Slf4j
public class HttpClientController {

    private static HttpClientUtil httpClientUtil = new HttpClientUtil();

    //http://localhost:8088/http-client/get?id=1
    @GetMapping("/http-client/get")
    public Object getRemoteUserGet(Integer id){
        String url = "http://localhost:8087/user/detail";
        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("id",id);
        String s = httpClientUtil.get(url, paramMap);
        System.out.println(s);
        DbUser dbUser = JSONObject.parseObject(s, DbUser.class);
        return dbUser;
    }

    //http://localhost:8088/http-client/post
    @GetMapping("/http-client/post")
    public Object getRemoteUserPost() throws Exception{
        String url = "http://localhost:8087/user/add";
        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("userName","jerry");
        paramMap.put("address","杭州");
        String resStr = httpClientUtil.post(url,null,paramMap);
        System.out.println(resStr);
        return resStr;
    }

}

5.3.4 测试调用效果

get请求

post请求

5.3.5 HttpClient配置优化

为了更好的管理HttpClient的连接配置,比如连接参数相关的设置,我们也可以使用配置类的方式对其进行优化,如下在配置文件中增加如下参数;

#最大连接数
http:
  maxTotal: 100
  #并发数
  defaultMaxPerRoute: 20
  #创建连接的最长时间
  connectTimeout: 2000
  #从连接池中获取到连接的最长时间
  connectionRequestTimeout: 5000
  #数据传输的最长时间
  socketTimeout: 10000
  #提交请求前测试连接是否可用
  staleConnectionCheckEnabled: true

增加一个HttpClientConfig配置类

package package com.congge.config;
 
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class HttpClientConfig {

    //最大连接数
    @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;
 
    /**
     * 实例化一个连接池管理器,设置最大连接数、并发连接数
     *
     * @return httpClientConnectionManager
     */
    @Bean(name = "httpClientConnectionManager")
    public PoolingHttpClientConnectionManager getHttpClientConnectionManager() {
        PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager();
        //设置最大连接数
        httpClientConnectionManager.setMaxTotal(maxTotal);
        //设置并发数
        httpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
        return httpClientConnectionManager;
    }
 
    /**
     * 实例化连接池,设置连接池管理器
     * 这里需要以参数形式注入上面实例化的连接池管理器
     *
     * @param httpClientConnectionManager 连接池管理器
     * @return httpClientBuilder 连接池
     */
    @Bean(name = "httpClientBuilder")
    public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager") PoolingHttpClientConnectionManager httpClientConnectionManager) {
        //HttpClientBuilder中的构造方法被protected修饰,所以这里不能直接使用new来实例化一个HttpClientBuilder,可以使用HttpClientBuilder提供的静态方法create()来获取HttpClientBuilder对象
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        httpClientBuilder.setConnectionManager(httpClientConnectionManager);
        return httpClientBuilder;
    }
 
    /**
     * 注入连接池,用于获取httpClient
     *
     * @param httpClientBuilder 连接池
     * @return 获取httpclient
     */
    @Bean
    public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder) {
        //        try {
//            //HttpClient设置忽略SSL,实现HTTPS访问, 解决Certificates does not conform to algorithm constraints
//            //http://www.manongjc.com/detail/13-cpmpdgbiafnliyo.html
//            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
//                @Override
//                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
//                    return true;
//                }
//            }).build();
//            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
//            return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return null;
        return httpClientBuilder.build();
    }
 
 
    /**
     * Builder是RequestConfig的一个内部类
     * 通过RequestConfig的custom方法来获取到一个Builder对象
     * 设置builder的连接信息
     * 这里还可以设置proxy,cookieSpec等属性,有需要的话可以在此设置
     *
     * @return builder
     */
    @Bean(name = "builder")
    public RequestConfig.Builder getBuilder() {
        RequestConfig.Builder builder = RequestConfig.custom();
        //将属性 set进 builder 中
        builder.setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(connectionRequestTimeout)
                .setSocketTimeout(socketTimeout)
                .setStaleConnectionCheckEnabled(staleConnectionCheckEnabled);
        return builder;
    }
 
    /**
     * 使用builder构建一个RequestConfig对象
     *
     * @param builder 连接信息
     * @return builder.build()
     */
    @Bean
    public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder) {
        return builder.build();
    }
 
}

 

5.4 RestTemplate使用

RestTemplate是Spring框架中的一个类,用于RESTful风格的HTTP请求调用。

  • 它提供了简单方便的方式来处理HTTP请求和响应,可以发送GET、POST、PUT、DELETE等类型的请求,并处理响应数据。

  • RestTemplate封装了常见的HTTP操作,使得开发者能够更加方便地与RESTful API进行交互。

  • 使用RestTemplate时,开发者可以通过调用不同的方法来发送HTTP请求,并根据需要设置请求头、请求参数、请求体等信息。同时,RestTemplate还支持将响应数据转换为Java对象或其他数据结构,简化了数据处理过程。

下面介绍RestTemplate的使用过程。

5.4.1 添加依赖

如果是springboot工程,默认情况下只要你添加了web依赖,就可以使用RestTemplate。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

5.4.2 添加初始化配置

也可以不配,有默认的设置

注意RestTemplate只有初始化配置,没有什么连接池

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        //默认的是JDK提供http连接,需要的话可以//通过setRequestFactory方法替换为例如Apache HttpComponents、Netty或//OkHttp等其它HTTP library。
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        //单位为ms
        factory.setReadTimeout(5000);
        //单位为ms
        factory.setConnectTimeout(5000);
        return factory;
    }
}

5.4.3 测试GET接口

使用restTemplate进行调用很灵活,API有多种形式,下面依次列举;

方式1:restTemplate.getForObject

使用这种方式,可以直接指定返回值类型,就不要对请求响应的结果单独处理了

    @Autowired
    private RestTemplate restTemplate;

    //http://localhost:8088/rest/getForObject?id=1
    @GetMapping("/rest/getForObject")
    public Object restGetRemote(Integer id){
        String url = "http://localhost:8087/user/detail?id="+id;
        DbUser dbUser = restTemplate.getForObject(url, DbUser.class);
        return dbUser;
    }

方式2:restTemplate.getForEntity

这种方式返回结果外面包装了一层ResponseEntity

    //http://localhost:8088/rest/getForEntity?id=1
    @GetMapping("/rest/getForEntity")
    public Object getForEntity(Integer id){
        String url = "http://localhost:8087/user/detail?id="+id;
        ResponseEntity<DbUser> responseEntity = restTemplate.getForEntity(url, DbUser.class);
        DbUser dbUser = responseEntity.getBody();
        return dbUser;
    }

方式3:restTemplate.exchange

这种方式比较灵活,即可以执行GET请求,POST请求,也可以自定义请求参数

写法1:

    //http://localhost:8088/rest/getExchange?id=1
    @GetMapping("/rest/getExchange")
    public Object getExchange(Integer id){
        String url = "http://localhost:8087/user/detail?id="+id;
        DbUser dbUser = restTemplate.exchange(url, HttpMethod.GET, null, DbUser.class).getBody();
        return dbUser;
    }

写法2:

通过ParameterizedTypeReference指定返回值类型

    //http://localhost:8088/rest/getExchange2?id=1
    @GetMapping("/rest/getExchange2")
    public Object getExchange2(Integer id){
        String url = "http://localhost:8087/user/detail?id="+id;
        //ParameterizedTypeReference<Map<String, Object>> responseBodyType = new ParameterizedTypeReference<Map<String, Object>>() {};
        ParameterizedTypeReference<DbUser> responseBodyType = new ParameterizedTypeReference<DbUser>() {};
        DbUser dbUser = restTemplate.exchange(url, HttpMethod.GET, null, responseBodyType).getBody();
        return dbUser;
    }

5.4.4 测试POST接口

方式1:

使用 restTemplate.postForObject

    //http://localhost:8088/rest/testPost1
    @GetMapping("/rest/testPost1")
    public Object getRemoteUserPost() throws Exception{
        String url = "http://localhost:8087/user/add";
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());

        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("userName","jerry");
        paramMap.put("address","杭州");
        String requestJson = JSONObject.toJSONString(paramMap);
        HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
        String result = restTemplate.postForObject(url, entity, String.class);
        System.out.println(result);
        return result;
    }

在这个过程中,如果响应中出现中文乱码,需要在配置类中添加一段配置:

	@Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(StandardCharsets.UTF_8));
        return restTemplate;
    }

方式2:

使用 restTemplate.postForEntity

@GetMapping("/rest/testPost2")
    public Object getRemoteUserPost2() throws Exception {
        String url = "http://localhost:8087/user/add";
//        Map<String, Object> requestMap = new HashMap<>();
//        requestMap.put("userName", "jerry");
//        requestMap.put("address", "杭州");
//        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestMap , String.class);

        DbUser dbUser = new DbUser();
        dbUser.setAddress("杭州");
        dbUser.setUserName("jerry");
        HttpEntity<DbUser> request = new HttpEntity<>(dbUser);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, request, String.class);

        return responseEntity.getBody();
    }

方式3:

使用 restTemplate.exchange

    @GetMapping("/rest/testPost3")
    public Object getRemoteUserPost3() throws Exception {
        String url = "http://localhost:8087/user/add";
        // 创建RestTemplate实例
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        DbUser dbUser = new DbUser();
        dbUser.setAddress("杭州");
        dbUser.setUserName("jerry");
        String requestBody =JSONObject.toJSONString(dbUser);
        HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<String> response = restTemplate.exchange(
                url,
                org.springframework.http.HttpMethod.POST,
                entity,
                String.class
        );
        // 输出响应体
        System.out.println(response.getBody());
        return response.getBody();
    }

5.5 RestClient使用

RestClient是在springboot3.X之后新增的一种http调用,可以说集成了上述所有http组件的优点,因此在使用之前,需要做两个准备:

  • 升级springboot版本;
  • 升级jdk为java17;

升级springboot

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version> <!-- 使用 Milestone 版本进行测试 -->
        <relativePath/>
    </parent>

测试接口

    RestClient restClient = RestClient.create();

    @GetMapping("/rest/client")
    public Object getFromRestClient(Integer id) {
        String url = "http://localhost:8087/user/detail?id=" + id;
        String body = restClient
                .get()
                .uri(url)
                .retrieve()
                .body(String.class);
        return body;
    }

六、写在文末

本文通过较大的篇幅详细介绍了java中常用的http组件的使用,http的调用可以说贯穿到日常开发中放方面面,因此熟练掌握http的组件非常重要,希望本篇对看到的同学有用,本篇到此结束,感谢观看。

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

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

相关文章

用 Python 创建 Voronoi 图

概述 最常见的空间问题之一是找到距离我们当前位置最近的兴趣点 (POI)。假设有人很快就会耗尽汽油&#xff0c;他/她需要在为时已晚之前找到最近的加油站&#xff0c;解决这个问题的最佳解决方案是什么&#xff1f;当然&#xff0c;驾驶员可以检查地图来找到最近的加油站&…

力扣每日一题-总行驶距离-2024.4.25

力扣题目&#xff1a;总行驶距离 题目链接: 2739.总行驶距离 题目描述 代码思路 直接用数学模拟计算即可 代码纯享版 class Solution {public int distanceTraveled(int mainTank, int additionalTank) {int sum 0;while(additionalTank > 0){if(mainTank > 5){mai…

CATO原理中的数学与魔术(六)——Baby Hummer的拓展一

在上一篇中&#xff0c;我们从CATO原理的数学讲解进入了魔术部分&#xff0c;介绍了其经典作品《Baby Hummer》&#xff0c;相关内容请戳&#xff1a; CATO原理中的数学与魔术&#xff08;五&#xff09;——Baby Hummer CATO原理中的数学与魔术&#xff08;四&#xff09;——…

leetcode 221 最大正方形面积

示例 3&#xff1a; 输入&#xff1a;matrix [["0"]] 输出&#xff1a;0 # 最大正方形面积 def max_square(matrix):m len(matrix)n len(matrix[0])if m 0 or n 0::return Nonemax_side 1dp [[0] * (n 1) for _ in range(m 1)]for i in range(1, m 1):fo…

2024全新瀚海跑道:矢量图片迅速养号游戏玩法,每天一小时,日转现200

最初我注意到这种玩法&#xff0c;是因为最近在浏览各大平台的视频时&#xff0c;我发现了一种特殊类型的账号&#xff0c;其养号成功率高达90%。这些账号发布的视频内容和数据非常夸张&#xff0c;而且制作起来非常简单&#xff0c;任何人都可以轻松上手。这些账号主要发布矢量…

Spring Web MVC入门(2)——请求

目录 一、传递单个参数 基础类型和包装类型的区别 1、基础类型 &#xff08;1&#xff09;不传参 &#xff08;2&#xff09;传字符串 2、包装类型 &#xff08;1&#xff09;不传参 &#xff08;2&#xff09;传字符串 3、小结 二、传递多个参数 三、传递对象 四、…

Leetcode_相交链表

✨✨所属专栏&#xff1a;LeetCode刷题专栏✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ 题目&#xff1a; 题解&#xff1a; 看到这个题目首先我们要排除链表逆置的想法&#xff0c;如图、因为c1节点只有一个next指针&#xff0c;逆置后不可能同时指向a2和b3节点。 其次有的的同学…

阿里前端常考vue面试题汇总_阿里高级vue面试题

改变 ![](https://img-blog.csdnimg.cn/img_convert/b736620bcd29f08f3685022ab5583d8b.webp?x-oss-processimage/format,png)你会发现&#xff0c; **只有改变的栏目才闪烁&#xff0c;也就是进行重绘** &#xff0c;数据没有改变的栏目还是保持原样&#xff0c;这样就大大节…

WebSocket 深入浅出

WebSocket 深入浅出 1. WebSocket 是什么2. WebSocket 建立连接通信的过程3. WebSocket 和http的联系与区别4. WebSocket 的使用场景及限制 1. WebSocket 是什么 定义&#xff1a;WebSocket 是一种网络通信协议&#xff0c;它允许在单个TCP连接上进行全双工通信。是HTML5规范提…

网络安全实训Day15

写在前面 电子垃圾&#xff0c;堂堂恢复连载。本来不想分天数梳理了&#xff0c;但是最后要写实训报告&#xff0c;报告里还要有实训日记记录每日学的东西&#xff0c;干脆发这里留个档&#xff0c;到时候写报告提供一个思路。 网络空间安全实训-渗透测试 渗透测试概述 定义 一…

关于conda占C盘内存的问题

文章目录 前言一、C盘中.conda文件中的envs二、C盘中.conda文件中的pkgs 前言 最近发现C盘空间越来越少&#xff0c;于是就去清理了一下conda在C盘的存储&#xff0c;不看不知道&#xff0c;一看吓一跳&#xff0c;足足十几G&#xff01;于是去网上搜索了相关的包能不能删除&a…

(3)C程序可执行文件的生成过程

原文链接&#xff1a;https://www.jianshu.com/p/b7e44f749211 一、可执行文件的生成 我们先通过一个简单C程序&#xff0c;回顾一下可执行文件的生成过程。 ​​​​​​​ ​​​​​​​ 可执行文件的生成过程如下图&#xff1a; 如图&#xff0c;可执行文…

------分割线之 WebSecurityConfigrerAdapter弃用问题------

WebSecurityConfigurerAdapter 被弃用的原因是 Spring Security 项目的维护者希望将项目的主要开发工作集中在新的配置方式上&#xff0c;即基于 Java 的配置&#xff08;Java Configuration&#xff09;和基于 Lambda 的表达式。这主要是因为 Spring 5.0 引入了重量级的 Java …

Windows系统下使用MySQL8.0.22创建第二套数据库

配置新的 MySQL 实例&#xff1a; 为了创建一个新的数据库实例&#xff0c;你需要复制 MySQL 的安装目录并创建一个新的数据目录和配置文件。假设你已经安装了 MySQL 在 C:\Program Files\MySQL\ 下&#xff0c;按照以下步骤操作&#xff1a; 复制整个 MySQL 文件夹&#xff0c…

【探索Java编程:从入门到入狱】Day2

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

STM32 USB HID报告描述符没有报告长度

STM32 USB HID设置(STM32CubeMX)_我也想成大侠的博客-CSDN博客 不影响鼠标功能

神经网络中多层卷积的作用

在神经网络中采用多层卷积的目的是为了逐步提取和组合图像的抽象特征&#xff0c;从而更有效地学习数据的表示并执行复杂的任务。不同层的卷积具有不同的作用&#xff0c;从较低层次的特征&#xff08;例如边缘、纹理&#xff09;到较高层次的抽象特征&#xff08;例如物体部件…

中国AI崛起!领先全球实现胰腺癌早筛,打破美国垄断!

人工智能在医疗领域的应用近年来备受关注&#xff0c;尤其是在癌症早筛领域。近期&#xff0c;斯坦福大学发布了《2024年AI指数报告》&#xff0c;透露2023年美国人工智能投资额为672亿美元&#xff0c;是中国的约8.7倍。其中&#xff0c;阿里巴巴达摩院&#xff08;湖畔实验室…

【Godot4.2】有序和无序列表函数库 - myList

概述 在打印输出或其他地方可能需要构建有序或无序列表。本质就是构造和维护一个纯文本数组。并用格式化文本形式&#xff0c;输出带序号或前缀字符的多行文本。 为此我专门设计了一个类myList&#xff0c;来完成这项任务。 代码 以下是myList类的完整代码&#xff1a; # …

AI对决:谷歌 VS 微软,谁更赚钱|TodayAI

尽管Alphabet和微软都发布了强劲的季度财报&#xff0c;显示两家科技巨头均超越了销售和利润的预期&#xff0c;但在生成式人工智能&#xff08;AI&#xff09;领域的投资回报方面&#xff0c;它们展现了不同的情况。Alphabet的CEO桑达尔皮查伊表示&#xff0c;他对Google通过出…