[SpringBoot3]远程访问@HttpExchange

六、远程访问@HttpExchange[SpringBoot3]

  • 远程访问是开发的常用技术,一个应用能够访问其他应用的功能。SpringBoot提供了多种远程访问的技术。基于HTTP协议的远程访问是最广泛的。
  • SpringBoot中定义接口提供HTTP服务。生成的代理对象实现此接口,代理对象实现HTTP的远程访问,需要理解:
    • @HttpExchange
    • WebClient

WebClient特性

  • 我们想要调用其他系统提供的HTTP服务,通常可以使用Spring提供的RestTemplate来访问,RestTemplate是SpringBoot3中引入的同步阻塞式HTTP客户端,因此存在一定性能瓶颈。Spring官方在Spring5中引入了WebClient作为非阻塞式HTTP客户端。
    • 非阻塞,异步请求
    • 它的响应式编程基于Reactor
    • 高并发,硬件资源少
    • 支持Java 8 lambdas函数式编程

什么是异步非阻塞

  • 异步和同步针对调用者,调用者发送请求,如果等待对方回应之后才去做其他事情,就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步
  • 阻塞和非阻塞针对被调度者,被调度者收到请求后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后去做事情,就是非阻塞。

6.1准备工作

  • 安装GsonFormat插件,方便json和Bean的转换

6.2声明式HTTP远程服务

  • 需求:访问https://jsonplaceholder.typicode.com/提供的todos服务。基于RESTful风格,增删改查。

1.Maven依赖pom.xml

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

    <!--WebClient-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <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>
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2.声明Todo数据类

@Data
public class Todo {
    private Integer userId;
    private Integer id;
    private String title;
    private Boolean completed;
}

3.声明服务接口

public interface TodoService {
    // 一个方法就是一个远程服务(远程调用)
    @GetExchange("/todos/{id}")
    Todo getTodoById(@PathVariable("id") Integer id);

    //增加资源
    @PostExchange(value = "/todos/", accept = MediaType.APPLICATION_JSON_VALUE)
    Todo createTodo(@RequestBody Todo newTodo);

    //修改资源
    @PutExchange("/todos/{id}")
    ResponseEntity<Todo> modifyTodo(@PathVariable Integer id, @RequestBody Todo todo);

    //删除资源
    @DeleteExchange("/todos/{sid}")
    void removeTodo(@PathVariable("sid") Integer id);
}

4.创建HTTP服务代理对象

//proxyBeanMethods = false:多实例对象,无论被取出多少此都是不同的bean实例,在该模式下SpringBoot每次启动会跳过检查容器中是否存在该组件
@Configuration(proxyBeanMethods = false)
public class HttpConfiguration {

    //创建服务接口的代理对象,基于WebClient
    @Bean
    public TodoService requestService() {
        WebClient webClient =
                WebClient.builder().baseUrl("https://jsonplaceholder.typicode.com").build();

        //创建代理工厂,设置超时时间
        HttpServiceProxyFactory proxyFactory =
                HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).blockTimeout(Duration.ofSeconds(60)).build();

        //创建某个接口的代理服务
        return proxyFactory.createClient(TodoService.class);
    }

}

5.单元测试

@SpringBootTest
class Springboot18HttpServiceApplicationTests {
    //注入代理对象
    @Resource
    private TodoService todoService;


    //测试访问todos/1
    @Test
    void testQuery() {
        Todo todo = todoService.getTodoById(1);
        System.out.println("todo = " + todo);
        System.out.println(todo.getTitle());
    }

    //创建资源
    @Test
    void testCreateTodo() {
        Todo todo = new Todo();
        todo.setId(1222);
        todo.setUserId(1223);
        todo.setTitle("事项1");
        todo.setCompleted(true);

        Todo res = todoService.createTodo(todo);
        System.out.println("res = " + res);
    }

    //修改资源
    @Test
    void testModify() {
        Todo todo = new Todo();
        todo.setId(1002);
        todo.setUserId(5002);
        todo.setTitle("事项2");
        todo.setCompleted(true);

        ResponseEntity<Todo> entity = todoService.modifyTodo(2, todo);
        HttpHeaders headers = entity.getHeaders();
        System.out.println("headers = " + headers);

        Todo body = entity.getBody();
        System.out.println("body = " + body);

        HttpStatusCode statusCode = entity.getStatusCode();
        System.out.println("statusCode = " + statusCode);
    }

    //删除资源
    @Test
    void testDelete() {
        todoService.removeTodo(10);
    }
}

6.3Http服务接口的方法定义

  • @HttpExchange注解用于声明接口作为HTTP远程服务。在方法、类级别使用。通过注解属性以及方法的参数设置HTTP请求的细节。

  • 快捷注解简化不同的请求方式:

    • GetExchange
    • PostExchange
    • PutExchange
    • PatchExchange
    • DeleteExchange
  • @GetExchange就是@HttpExchange表示的GET请求方式
    在这里插入图片描述

  • 作为HTTP服务接口中的方法允许使用的参数列表

  • 接口中方法返回值

6.4组合使用注解

  • @HttpExchange、@GetExchange等可以组合使用。

1.创建Albums数据类

@Data
public class Albums {
    private Integer id;
    private Integer userId;
    private String title;
}

2.创建AlbumsService接口

  • 接口声明方法,提供HTTP远程服务。
@HttpExchange(url = "https://jsonplaceholder.typicode.com/")
public interface AlbumsService {

    //查询专辑
    @HttpExchange(method = "GET",url = "/albums/{id}")
    Albums getById(@PathVariable Integer id);
}

3.声明代理

@Bean
//创建代理
public AlbumsService albumsService() {
    WebClient webClient = WebClient.create();
    HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).blockTimeout(Duration.ofSeconds(60)).build();
    return proxyFactory.createClient(AlbumsService.class);
}

4.单元测试

@SpringBootTest
public class AlbumsServiceTest {
    @Resource
    private AlbumsService albumsService;

    @Test
    void testQuery() {
        Albums albums = albumsService.getById(5);
        System.out.println("albums = " + albums);
    }
}

6.5Java Record

  • 测试Java Record作为返回类型。

创建Albums的Java Record

public record AlbumsRecord(Integer id, Integer userId, String title) {
}

其余步骤一样

6.6定制HTTP请求服务

  • 设置HTTP远程的超时时间,异常处理
  • 在创建接口代理对象前,先设置WebClient的有关配置。

1.设置超时,异常处理

//定制HTTP服务
@Bean
public AlbumsService albumsService() {
    //超时
    HttpClient httpClient = HttpClient.create()
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000)//连接时间
            .doOnConnected(conn -> {
                conn.addHandlerLast(new ReadTimeoutHandler(10));//读超时
                conn.addHandlerLast(new WriteTimeoutHandler(10));//写超时
            });
    //设置异常
    WebClient webClient = WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(httpClient))
        //定制 4XX,5XX 的回调函数
            .defaultStatusHandler(HttpStatusCode::isError, clientResponse -> {
                System.out.println("WebClient请求异常");
                return Mono.error(new RuntimeException("请求异常" + clientResponse.statusCode().value()));
            }).build();

    HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).blockTimeout(Duration.ofSeconds(60)).build();
    return proxyFactory.createClient(AlbumsService.class);
}

2.单元测试

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ja kar ta

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值