【2023】java常用HTTP客户端对比以及使用(HttpClient/OkHttp/WebClient)

💻目录

1、介绍

现在java使用的http客户端主要包括以下几种

而这些中使用得最频繁的主要是:

  1. Apache HttpClient:这是一个功能强大且广泛使用的第三方库,用于进行HTTP通讯。它提供了更高级的API和更丰富的功能,比如支持连接池、认证、重定向、Cookie管理等。Apache HttpClient可以作为独立的库使用,也可以作为Apache HttpComponents项目的一部分。

  2. OkHttp:这是另一个流行的第三方库,用于进行HTTP通讯。OkHttp提供了简洁的API和高性能的特性,支持同步和异步请求,以及连接池、缓存、拦截器等功能。OkHttp也是Square公司的一个开源项目。

  3. Spring WebClient:这是Spring框架中的一个模块,是RestTemplate的升级版,用于进行非阻塞的HTTP通讯。它基于Reactive Streams编程模型,适用于构建响应式的应用程序。Spring WebClient提供了简单的API来发送HTTP请求和处理响应,可以与Spring WebFlux等模块一起使用。

2、使用

下面展示了Apache HttpClient和OkHttp以及Spring WebClient的常用使用方式,包括get和post

2.1、添加配置

2.1.1、依赖

xml 复制代码
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
        <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5</artifactId>
        </dependency>
	 <!--okhttp-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <!--webClient-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

2.1.2、工具类

  • Result:统一返回类
java 复制代码
@Data
public class Result<T> {
    //状态码
    private Integer code;
    //信息
    private String message;
    //数据
    private T data;

    private Result(){}

    //设置数据,返回对象的方法
    public static <T> Result<T> build(T data, ResultCodeEnum resultCodeEnum) {
        //创建Result对象,设置值,返回对象
        Result<T> result = new Result<>();
        //判断返回结果中是否需要数据
        if (data != null) {
            //设置数据到result对象
            result.setData(data);
        }
        //设置其他值
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        //返回设置值之后的对象
        return result;
    }

    //成功的方法
    public static <T> Result<T> ok(T data) {
        return build(data, ResultCodeEnum.SUCCESS);
    }

    //成功的方法
    public static <T> Result<T> ok() {
        return build(null, ResultCodeEnum.SUCCESS);
    }

    //失败的方法
    public static <T> Result<T> fail(T data) {
        return build(data, ResultCodeEnum.FAIL);
    }
}
  • ResultCodeEnum:返回结果
java 复制代码
@Getter
public enum  ResultCodeEnum {

    SUCCESS(200,"成功"),
    FAIL(201, "失败"),
    ;

    private Integer code;
    private String message;

    ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

2.1.3、实体

  • User
java 复制代码
@Data
public class User {
    private String name;
    private Integer age;
    private Integer post;
}
  • ProductInfo:返回数据
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class ProductInfo implements Serializable {

    private Long id;
    /**销量*/
    private Integer sale;

    /**价格*/
    private Integer price;
    /**名称*/
    private String name;
    /**类型*/
    private Integer type;
    /**端口*/
    private Integer port;
}

2.1.4、Controller接口

java 复制代码
@RestController
@RequestMapping("/productInfo/index")
public class ProductInfoController {

    @Resource
    private ProductInfoService productInfoService;

    //    获取当前服务的端口
    @Value("${server.port}")
    private Integer port;

    @GetMapping("/get")
    public Result<?> get(Integer type){
        List<ProductInfo> productInfoList = getProductInfoList(type);
        System.out.println(productInfoList);
        return Result.ok(productInfoList);
    }


    @PostMapping("/post")
    public Result<?> post(@RequestBody User user){
        Integer post = user.getPost();

        List<ProductInfo> productInfoList = getProductInfoList(post);
        System.out.println(productInfoList);
        return Result.ok(productInfoList);
    }


    public List<ProductInfo> getProductInfoList(Integer type) {

        ProductInfo productInfo3 = new ProductInfo(3l,800,20,"哈密瓜1",1,port);
        ProductInfo productInfo1 = new ProductInfo(1l,50,8,"苹果1",1,port);
        ProductInfo productInfo2 = new ProductInfo(2l,200,13,"牛肉1",2,port);
        ProductInfo productInfo4 = new ProductInfo(4l,50,9,"青菜1",2,port);
        /*        实际项目中,会从数据库查出数据 */
        List<ProductInfo> productInfoList = Arrays.asList(productInfo1,productInfo2,productInfo3,productInfo4);

//        根据传入的类型返回指定类型
        List<ProductInfo> result = productInfoList.stream()
                .filter(productInfo -> productInfo.getType() == type)
                .collect(Collectors.toList());
        return result;
    }
}

2.2、Apache HttpClient使用

  • get使用
    默认只能同步异步需要加额外的依赖,get请求不能携带参数,只能通过拼接路径
xml 复制代码
        <!--异步请求-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpasyncclient</artifactId>
        </dependency>
java 复制代码
    public static void get(CloseableHttpClient httpClient,String url){
//            连接对象
        try {
//        HttpGet不能携带参数,如果需要参数只能通过拼接
            HttpGet httpGet = new HttpGet(url+"?type=1");

            String execute = httpClient.execute(httpGet, response -> {
                JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
                Result result = JSONUtil.toBean(entries, Result.class);
                if (result.getCode() == 200) {
                    String data = result.getData().toString();
                    System.out.println(data);
                    return data;
                } else {
                    System.err.println(result.getMessage());
                    return result.getMessage();
                }
            });
            System.out.println("get成功结果:"+execute);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • post
    post请求,通过setEntity()给HttpPost对象添加请求对象
java 复制代码
    public static void post(CloseableHttpClient httpClient,String url){

        HttpPost httpPost = new HttpPost(url);
        User user = new User();
        user.setPost(1);
//        添加参数对象
        httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(user)));
        // 设置请求头
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");

        try {
            String execute = httpClient.execute(httpPost, response -> {
                JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
                Result result = JSONUtil.toBean(entries, Result.class);
                if (result.getCode() == 200) {
                    String data = result.getData().toString();
                    log.info(data);
                    return data;
                } else {
                    log.error(result.getMessage());
                    return result.getMessage();
                }
            });
            log.info("HttpClient的post成功结果:"+execute);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 全部代码

执行步骤

  1. 创建CloseableHttpClient对象
  2. 创建get获取post请求对象
  3. 通过execute()方法发送请求
  4. 通过response.getEntity()获取响应回来的数据
java 复制代码
@Slf4j
public class HttpClientUtil {

    private static String postUrl = "http://localhost:8202/productInfo/index/post";
    private static String getUrl = "http://localhost:8202/productInfo/index/get";

    public static void main(String[] args) {
        try {
//            创建客户端对象
            CloseableHttpClient client = HttpClients.createDefault();
            get(client,getUrl);
            post(client,postUrl);
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void get(CloseableHttpClient httpClient,String url){
//            连接对象
        try {
//        HttpGet不能携带参数,如果需要参数只能通过拼接
            HttpGet httpGet = new HttpGet(url+"?type=1");

            String execute = httpClient.execute(httpGet, response -> {
                JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
                Result result = JSONUtil.toBean(entries, Result.class);
                if (result.getCode() == 200) {
                    String data = result.getData().toString();
                    log.info(data);
                    return data;
                } else {
                    log.info(result.getMessage());
                    return result.getMessage();
                }
            });
            log.info("HttpClient的get成功结果:"+execute);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void post(CloseableHttpClient httpClient,String url){

        HttpPost httpPost = new HttpPost(url);
        User user = new User();
        user.setPost(1);
//        添加参数对象
        httpPost.setEntity(new StringEntity(JSONUtil.toJsonStr(user)));
        // 设置请求头
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");

        try {
            String execute = httpClient.execute(httpPost, response -> {
                JSONObject entries = new JSONObject(EntityUtils.toString(response.getEntity()));
                Result result = JSONUtil.toBean(entries, Result.class);
                if (result.getCode() == 200) {
                    String data = result.getData().toString();
                    log.info(data);
                    return data;
                } else {
                    log.error(result.getMessage());
                    return result.getMessage();
                }
            });
            log.info("HttpClient的post成功结果:"+execute);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.3 、OkHttp使用

执行步骤

  1. 创建OkHttpClient对象,用于配置和管理HTTP请求的行为
  2. 创建Request对象,用于描述要发送的HTTP请求。Request对象包含了URL、请求方法、请求头、请求体等信息。
  3. 创建Call对象,使用OkHttpClient的newCall()方法,传入Request对象,创建一个Call对象,Call对象表示一次HTTP请求的调用,可以用于执行同步或异步的请求。
  4. 执行请求,同步通过Call对象的execute()方法来;异步通过Call对象的enqueue()方法来执行请求,并通过回调函数处理响应
  5. 解析响应,通过response.body()得到响应的内容在通过json解析。
java 复制代码
@Slf4j
public class OkHttpUtil {

    private static String postUrl = "http://localhost:8202/productInfo/index/post";
    private static String getUrl = "http://localhost:8202/productInfo/index/get";


    public static void main(String[] args) {
        OkHttpClient client = new OkHttpClient().newBuilder()
                .connectTimeout(30, TimeUnit.SECONDS) //连接超时时间
                .writeTimeout(30,TimeUnit.SECONDS)  //请求超时时间
                .readTimeout(30,TimeUnit.SECONDS)  //响应超时时间
                .build();
//        get(client);
//        asyncGet(client);
//        post(client);
        asyncPost(client);
    }

    /**
     * 同步get
     * @param client        
     * @return void
     */
    public static void get(OkHttpClient client){
//        创建get请求对象
        Request request = new Request.Builder()
                .url(getUrl+"?type=1")
                .get()
                .header("Content-Type", "application/json") // 设置Content-Type请求头
                .build();

        try {
//            返回响应对象
            Response response = client.newCall(request).execute();
//              验证响应是否成功
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
//              解码对象
            Result result = JSONUtil.toBean(response.body().string(), Result.class);
            Object data = result.getData();
            System.out.println(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 异步get
     * @param client
     * @return void
     */
    public static void asyncGet(OkHttpClient client){
        //        创建get请求对象
        Request request = new Request.Builder()
                .url(getUrl+"?type=1")
                .get()   //不指定请求方式默认是get
                .build();
//           异步发送请求,没有返回结果
        client.newCall(request).enqueue(new Callback() {

//               处理失败请求
            @Override
            public void onFailure(Call call, IOException e) {
                log.debug("Unexpected code " + e.getMessage());
                e.printStackTrace();
            }

//            处理成功请求
            @Override
            public void onResponse(Call call, Response response) throws IOException {
//              验证响应是否成功
                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
//              解码对象
                Result result = JSONUtil.toBean(response.body().string(), Result.class);
                Object data = result.getData();
                System.out.println(data);
            }
        });
    }


    /**
     * 同步post
     * @param client
     * @return void
     */
    public static void post(OkHttpClient client){
        User user = new User();
        user.setPost(1);

        String str = JSONUtil.toJsonStr(user);
        Request request = new Request.Builder()
                .url(postUrl)
                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), str))
                .build();

        Call call = client.newCall(request);
        try {
            Response response = call.execute();
            //              验证响应是否成功
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
//              解码对象
            Result result = JSONUtil.toBean(response.body().string(), Result.class);
            Object data = result.getData();
            log.info("post请求成功,返回结果:{}",data);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 异步post请求
     * @param client        
     * @return void
     */
    public static void asyncPost(OkHttpClient client){
        User user = new User();
        user.setPost(1);
//          把对象转为json字符串
        String str = JSONUtil.toJsonStr(user);

        Request request = new Request.Builder()
                .url(postUrl)
                .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), str))
                .build();

        Call call = client.newCall(request);
//       异步请求没返回
        call.enqueue(new Callback() {
//            请求异常
            @Override
            public void onFailure(Call call, IOException e) {
                log.debug("Unexpected code " + e.getMessage());
                e.printStackTrace();
            }
//            请求成功
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //              验证响应是否成功
                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
//              解码对象
                Result result = JSONUtil.toBean(response.body().string(), Result.class);
                Object data = result.getData();
                log.info("异步post请求成功,返回结果:{}",data);
            }
        });
    }
}

2.4、WebClient使用

执行步骤:

  1. 创建WebClient对象,推荐使用WebClient.builder()的方式创建,因为可以自定义配置客户端的参数,直接new是只能使用默认的配置。

  2. 构建请求,直接通过webClient对象,如get()、post()等,构建一个请求(如果没有指定请求方式),默认是get请求。

  3. 处理响应:使用响应对象(如Mono、Flux等)来处理响应数据。你可以通过操作符(如map()、flatMap()等)对响应进行转换、过滤、合并等操作。

  4. 订阅响应:使用subscribe()方法来订阅响应流,启动请求并处理响应数据。你可以通过回调函数或操作符链来处理响应数据。

java 复制代码
@Component
@Slf4j
public class WebClientUtil {
    private static String postUrl = "http://localhost:8202/productInfo/index/post";
    private static String getUrl = "http://localhost:8202/productInfo/index/get";



    /**
     * 同步执行get请求
     * @param webClient
     * @return void
     */
    public void get(WebClient webClient){
        Mono<Result> mono = webClient.get()
                .uri(getUrl + "?type=2")
                .retrieve()
                .bodyToMono(Result.class);

//        获取返回结果 block()会进行堵塞,等待返回结果
        Result result = mono.block();
        if (result.getCode()==200){
            log.info("get请求返回结果{}",result.getData());
        }
    }

    /**
     * 异步get
     * @return void
     */
    public void asyncGet(){
        HttpClient client = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)  //设置连接超时
                .doOnConnected(conn -> conn
                        .addHandler(new ReadTimeoutHandler(10, TimeUnit.SECONDS)) //写入超时
                        .addHandler(new WriteTimeoutHandler(10))); //读取超时


        //        也可以以这种方式创建WebClient可以添加请求头和url以及一些参数;
        WebClient webClient = WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(client))
                .baseUrl(getUrl)

                .defaultHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)")
//                .defaultCookie()  //添加请求Cookie
                .build();

//        获取返回结果
        Mono<Result> mono = webClient.get()
                .uri( "?type=2")// 请求路径,会拼接上面的
                .accept(MediaType.APPLICATION_JSON)

//                .retrieve() //获取响应体
//                .bodyToMono(Result.class);  //指定获取的类型,直接获取结果。

//                或者通过该方式手动处理结果
                .exchangeToMono(response->{
                    if (response.statusCode().equals(HttpStatus.OK)) {
//                        成功返回
                        return response.bodyToMono(Result.class);
                    }
                    else {
//                        失败返回
                        return response.createException().flatMap(Mono::error);
                    }
                });


//        异步获取返回结果
        mono
                .subscribe(result -> {

                    if (result.getCode() == 200) {
                        log.info("get请求返回结果{}", result.getData());
                    }
                });

        System.out.println("执行完成");

    }
    
    /**
     * 同步post
     * @return void
     */
    public void post(){
        // 创建 WebClient 对象
        WebClient webClient = WebClient.builder()
                .baseUrl(getUrl)
                .build();
        User user = new User();
        user.setPost(2);

        Mono<Result> mono = webClient
                .post()
                .uri(postUrl)
                .contentType(MediaType.APPLICATION_JSON)
                .header("token","12321412")
                .body(BodyInserters.fromValue(user))
                .retrieve()
                .bodyToMono(Result.class);
        Result result = mono.block();
        if (result.getCode()==200){
            log.info("post请求返回结果{}",result.getData());
        }
    }


    /**
     * WebClient异步请求
     * @return void
     */
    public void asyncPost(){
        // 创建 WebClient 对象
        WebClient webClient = WebClient.builder()
                .baseUrl(getUrl)
                .build();
        User user = new User();
        user.setPost(2);

        Mono<Result> mono = webClient
                .post()
                .uri(postUrl)
                .contentType(MediaType.APPLICATION_JSON)  //指定类型
                .header("token","12321412")  //添加请求头
                .body(BodyInserters.fromValue(user)) //添加请求对象
                .retrieve()
                .bodyToMono(Result.class);
//          异步请求
        mono.subscribe(result -> {
            if (result.getCode()==200){
                log.info("post异步请求返回结果{}",result.getData());
            }
        });
    }
}
相关推荐
风铃儿~2 分钟前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
斯普信专业组7 分钟前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿17 分钟前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA30 分钟前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
gaoliheng00639 分钟前
Redis看门狗机制
java·数据库·redis
我是唐青枫41 分钟前
.NET AOT 详解
java·服务器·.net
Su米苏1 小时前
Axios请求超时重发机制
java
本郡主是喵2 小时前
并发编程 - go版
java·服务器·开发语言
南风lof2 小时前
源码赏析:Java线程池中的那些细节
java·源码阅读