唯一一个优雅100分的Http框架

4,568 阅读8分钟

导读:本文从AI时代背景下的软件架构变化谈起,引出其中一个必要的Http客户端技术,进而分析当前Java领域主要的Http客户端和Http框架,并重点阐述了其中优雅度满分的Retrofit框架,以及相关的使用技巧。通过阅读本文,你可以快速了解Java领域的三大Http客户端是什么?三大封装框架是什么?以及熟练掌握Retrofit框架的各种使用技巧。

前沿

在AI技术日新月异的今天,软件架构正经历着前所未有的变革,以适应更加复杂、高效且智能的数据交互需求。作为这一变革中的重要一环,HTTP客户端技术成为了连接前后端、大模型与Java、微服务之间不可或缺的桥梁。在Java这一广泛应用的编程语言中,HTTP客户端的选择与使用不仅关乎到系统的性能与稳定性,还直接影响到开发效率与代码的可维护性。因此对于开发者掌握好Http客户端和框架势必重要!

三大框架是什么?

在Java生态中,三大主流的HTTP客户端分别是:

  • Apache HttpClient:作为Apache软件基金会下的一个项目,HttpClient以其丰富的功能、高度的可配置性和广泛的社区支持而闻名。它支持HTTP/1.1和HTTP/2协议,提供了强大的请求执行、响应处理以及连接管理功能。
  • OkHttp:由Square公司开发,OkHttp以其高效、易用和强大的错误处理能力而受到青睐。它支持同步和异步请求,内置了连接池、GZIP压缩和HTTP/2等特性,非常适合在Android和Java后端服务中使用。
  • HttpUrlConnection:随着Java 11的发布,Java平台内置了一个全新的HTTP客户端API。这个API旨在提供简单、现代的HTTP客户端功能,同时保持与Java平台的紧密集成。它支持HTTP/2和WebSocket,并提供了异步和流式API,使得处理HTTP请求变得更加灵活和高效。

这三大客户端封装实现了HTTP协议,但是在封装的API方法上千差万别,且都不怎么好用,因此开源社区又陆续的出现了三大HTTP框架:

  • Spring RestTemplate(尽管Spring 5引入了WebClient作为更现代的替代品,但RestTemplate在旧项目中仍广泛使用):RestTemplate是Spring框架提供的一个同步客户端,用于简化与HTTP服务的通信。它提供了丰富的模板方法,用于处理HTTP请求,并自动将响应体绑定到Java对象上。

  • Retrofit(注意:虽然Retrofit在Android开发中非常流行,但严格来说它更多被用于Android而非纯Java后端环境。不过,其设计理念和优雅度值得借鉴):Retrofit是一个类型安全的HTTP客户端,用于Android和Java,它基于OkHttp构建,通过注解的方式极大地简化了HTTP请求的编写。Retrofit将HTTP请求抽象为Java接口,开发者只需定义接口方法并添加注解,即可实现复杂的HTTP请求。

  • Feign:Feign是一个声明式的Web服务客户端,它使得编写Web服务客户端变得更加简单。Feign通过创建接口和注解的方式来定义服务绑定,它内部封装了Ribbon和Hystrix,提供了负载均衡和断路器功能,非常适合在微服务架构中使用。

企业开发者往往都使用更为简单的框架,而这3大框架都为主流的,但是他们各自有很明显的区别:

  • RestTemplate:Spring把HttpClient、OkHttp、HttpUrlConnection的使用统一成一套API,只做了统一,并API使用上并没有简化太多
  • Retrofit:采用声明式方式定义http请求,使得调用http请求变成了对方法进行调用,非常优雅的方式供开发者使用
  • Feign:同Retrofit一样,采用声明式的方式定义http请求,但多用于微服务之间通信

在实践开发中,如果开发的是微服务项目则可首选Feign,因为它还支持更多微服务所需要的功能。而如果在单体项目中则首选Retrofit,其优雅的使用方式能大大简化接口的开发、管理**。同时Retrofit底层通过OkHttp进行实现,性能相比其它框架最好。**

Retrofit框架是什么?

官网:square.github.io/retrofit/

Retrofit基于OkHttp开发的、类型安全的Android或Java Http框架。再此我们选用Retrofit框架的重点理由是它基于OkHttp封装,性能好,同时它融入了流行的声明式(PS:只需要定义,不需要实现)开发思想,便于我们开发和学习。

image-20240816203335315.png

Retrofit入门

在Retrofit中要实现一个Http请求总体来说共有以下2个步骤:

  • 1、导入依赖
  • 2、声明API
  • 3、生效API

导入依赖

<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>retrofit</artifactId>
    <version>2.11.0</version>
</dependency>

声明API

我们可以先创建一个接口类:cn.itcast.star.graph.comfyui.client.api.ComfyuiApi,并按如下代码进行编写:

package cn.itcast.star.graph.comfyui.client.api;

import retrofit2.Call;
import retrofit2.http.GET;

import java.util.HashMap;

public interface ComfyuiApi {

    /**
     * 获取系统信息
     * @return
     */
    @GET("/system_stats")
    Call<HashMap> getSystemStats();

}

使用Retrofit来声明API,其实就是声明接口方法,比如上述类中定义了一个方法getSystemStats:

  • 方法名可以任意取,但建议与http接口名称一致
  • 方法通过@GET注解声明当前请求方式是GET并且请求的地址为/system_stats,即获取Comfyui的系统信息接口
  • 方法返回结果为Call<HashMap>对象
    • Call是固定的返回对象
    • HashMap则是接口返回的数据自动转成Map集合存储

生效API

上面声明类要生效,我们还需要配置生效,可创建一个配置类:cn.itcast.star.graph.comfyui.client.config.ComfyuiConfig

package cn.itcast.star.graph.comfyui.client.config;

import cn.itcast.star.graph.comfyui.client.api.ComfyuiApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import retrofit2.Retrofit;
import retrofit2.converter.jackson.JacksonConverterFactory;

import java.io.IOException;

@Configuration
public class ComfyuiConfig {

    @Bean
    public ComfyuiApi comfyuiApi() throws IOException {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://192.168.100.129:8188")
                .addConverterFactory(JacksonConverterFactory.create())
                .build();
        ComfyuiApi comfyuiApi = retrofit.create(ComfyuiApi.class);
        return comfyuiApi;
    }

}

在类中:

  • 首先通过Retrofit.Builder构建一个Retrofit客户端
    • 通过baseUrl指定请求的服务器地址
    • 通过addConverterFactory指定请求数据的转换器
  • 最后调用 retrofit.create方法创建ComfyuiApi接口的实现

通过上述代码,在Spring IOC中就是声明好了一个可以远程调用获取Comfyui服务器状态的Bean.

测试API

在测试包下创建类:cn.itcast.star.graph.ComfyuiApiTest

package cn.itcast.star.graph;

import cn.itcast.star.graph.comfyui.client.api.ComfyuiApi;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ComfyuiApiTest {

   @Autowired
   ComfyuiApi comfyuiApi;

   @Test
   public void test() throws Exception {
       System.out.println(comfyuiApi.getSystemStats().execute().body());
   }

}

在类中,直接注入ComfyuiApi,并调用getSystemStats(),即可发起请求并打印出结果:

image-20240817165626054.png

Retrofit 注解大全

Retrofit为了应对更复杂的Http请求,提供了丰富的注解来支持:

  • @Query:用来声明http查询部分的参数,与@RequestParam类似

  • @Path:用于说明请求路径参数,与@PathVariable类似

    @GET("group/{id}/users")
    Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
    
  • @Body:用于声明请求体对象,与@RequestBody类似

    @POST("users/new")
    Call<User> createUser(@Body User user);
    
  • @Multipart:用于说明当前请求以表单形式发起,常常用于带有文件的接口

    • 如果是文件字段需要使用 MultipartBody.Part
    • 如果是非文件字段需要使用 RequestBody
  • @Part:用于说明一个表单字段

    @Multipart
    @PUT("user/photo")
    Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
    
  • @Headers:设定请求头信息

    @Headers({
        "Accept: application/vnd.github.v3.full+json",
        "User-Agent: Retrofit-Sample-App"
    })
    @GET("users/{username}")
    Call<User> getUser(@Path("username") String username);
    
  • @Header:设定一个头字段

    @GET("user")
    Call<User> getUser(@Header("Authorization") String authorization)
    
  • @HeaderMap :把一个Map内容设定到请求头中

    @GET("user")
    Call<User> getUser(@HeaderMap Map<String, String> headers)
    

Retrofit 高级配置

日志开启

Retrofit还提供了打印日志的功能,方便我们进行BUG的排错。Retrofit日志功能默认是关闭的,可以通过以下代码进行开启:

@Bean
public ComfyuiApi comfyuiApi() throws IOException {
    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(loggingInterceptor)
        .retryOnConnectionFailure(true)
        .connectTimeout(30, TimeUnit.SECONDS)
        .build();

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://192.168.100.129:8188")
        .client(okHttpClient)
        .addConverterFactory(JacksonConverterFactory.create())
        .build();
    ComfyuiApi comfyuiApi = retrofit.create(ComfyuiApi.class);
    return comfyuiApi;
}

我们之前提供过Retrofit框架底层是通过OkHttp实现,Retrofit的日志功能也是交由底层OkHttp实现:

  • 由于要重新设置OkHttp,因此代码中自行构建了OkHttpClient
  • 给OkHttpClient增加了日志拦截器HttpLoggingInterceptor,并设置拦截器的日志级别为BODY
    • NONE:不输出
    • BASIC:输出基本摘要
    • HEADERS:输出头信息
    • BODY:输出body数据
  • 最后第14行通过client方法,重新设定Retrofit底层使用的OkHttp客户端

按上述代码进行修改后,重新运行ComfyuiApiTest类,即可在控制台看见相关的日志输出:

image-20240817172756855.png

修改数据转换器

Retrofit支持把Http的数据直接转换成各种对象、各种数据格式,要使用那种格式,可通过配置数据转换器自行选择,目前Retrofit支持8种:

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • JAXB: com.squareup.retrofit2:converter-jaxb
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

在项目中如果要使用GSON,则先需要导入对应的依赖:

<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>converter-gson</artifactId>
    <version>2.11.0</version>
</dependency>

然后在代码中通过addConverterFactory方法进行,设定转换工厂类:

工厂类命令为XXXConverterFactory,如果是Jackson则为JacksonConverterFactory,以此类推。

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

关注我学Java+AI前沿技术

🔥【Java+AI燃爆新项目】“星图”剧透来袭!🌟

解锁Stable Diffusion、ComfyUI、Ollama等前沿科技,融合Retrofit2与双端异步通信,还有集群队列调度的智慧加成!🚀

想掌握AI应用的核心竞争力吗?快来围观我们的课程项目,一起打造未来科技的璀璨之星!✨ 👀别错过,关注我们,开启你的AI学习新篇章!📚

企业微信截图_1728381063841.png

企业微信截图_17283812187956.png

企业微信截图_17283812313044.png

企业微信截图_17283812652336.png