绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
Spring6的新特性,正式“抛弃”feign了
2023-02-16 10:22:03


来源:juejin.cn/post/7173271507047546893
多数网友在对接第三方系统时,还在使用 httpclient、okhttp 等方式进行硬编码的形式实现对接。其实,我认为使用 feign 来实现更为方便快捷。feign 除了服务间的 http 调用外,还支持非服务间的 http 调用。只是,我们的一些思维还未转变过来,对 feign 的底层实现也缺乏一定的了解。
近期,Spring 6 的个 GA 版本发布了,其中带来了一个新的特性——HTTP Interface。这个新特性,可以让开发者将 HTTP 服务,定义成一个包含特定注解标记的方法的 Java 接口,然后通过对接口方法的调用,完成 HTTP 请求。看起来很像使用 Feign 来完成远程服务调用,这可能是 Spring “抛弃” feign 等 http 调用的一种趋势,Spring 要规范和简化这一块的实现了,让开发者更关注聚集于业务。下面我们参考官方文档来完成一个 Demo。

完成一个 Demo

首先创建一个简单的 HTTP 服务,这一步可以创建一个简单的 Spring Boot 工程来完成。
先创建一个实体类:
public class User implements Serializable {

    private int id;
    private String name;
    // 省略构造方法、Getter和Setter
    @Override
    public String toString() {
        return id + ":" + name;
    }
}
再写一个简单的 Controller:
@GetMapping("/users")
public List<User> list() {
    return IntStream.rangeClosed(110)
            .mapToObj(i -> new User(i, "User" + i))
            .collect(Collectors.toList());
}
确保启动服务之后,能够从http://localhost:8080/users地址获取到一个包含十个用户信息的用户列表。
下面我们新建一个 Spring Boot 工程。
Spring initializr
这里需要注意,Spring Boot 的版本至少需要是 3.0.0,这样它以来的 Spring Framework 版本才是 6.0 的版本,才能够包含 HTTP Interface 特性,另外,Spring Framework 6.0 和 Spring Boot 3.0 开始支持的 Java 版本低是 17,因此,需要选择至少是 17 的 Java 版本。
另外,需要依赖 Spring Web 和 Spring Reactive Web 依赖,原因下文中会提到。
创建好新的 Spring Boot 工程后,首先需要定义一个 HTTP Interface 接口。简单的定义如下即可:
public interface UserApiService {
    @GetExchange("/users")
    List<User> getUsers();
}
然后,我们可以写一个测试方法。
@Test
void getUsers() {
   WebClient client = WebClient.builder().baseUrl("http://localhost:8080/").build();
   HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
   UserApiService service = factory.createClient(UserApiService.class);
   List<User> users = service.getUsers();
   for (User user : users) {
      System.out.println(user);
   }
}
终回打印获取到的是个用户信息:
1:User1
2:User2
...
9:User9
10:User10
以上是一个简单的示例,下面我们看看其中的一些细节。

GetExchange(HttpExchange)注解

上文例子中的 GetExchange 注解代表这个方法代替执行一个 HTTP Get 请求,与此对应,Spring 还包含了其他类似的注解:
GetExchange
这些注解定义在spring-web模块的org.springframework.web.service.annotation包下,除了 HttpExchange 之外,其他的几个都是 HttpExchange 的特殊形式,这一点与 Spring MVC 中的 RequestMapping/GetMapping 等注解非常相似。
以下是 HttpExchange 的源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective(HttpExchangeReflectiveProcessor.class)
public @interface HttpExchange 
{

    @AliasFor("url")
    String value() default "";

    @AliasFor("value")
    String url() default "";

    String method() default "";

    String contentType() default "";

    String[] accept() default {};

}
在上面的例子中,我们只指定了请求的资源路径。

UserApiService 实例的创建

在上面例子中,我们定义的 HTTP Interface 接口是 UserApiService,在测试方法中,我们通过 HttpServiceProxyFactory 创建了 UserApiService 的实例,这是参考了 Spring 的官方文档的写法。
你也可以将创建的过程写到一个 @Bean 方法中,从而可以将创建好的实例注入到其他的组件中。
我们再定义 UserApiService 的时候,只是声明了一个接口,那具体的请求操作是怎么发出的呢,我们可以通过 DEBUG 模式看得出来,这里创建的 UserApiService 的实例,是一个代理对象:
代理对象
目前,Spring 还没有提供更方便的方式来创建这些代理对象,不过,之后的版本肯定会提供,如果你感兴趣的话,可以从 HttpServiceProxyFactory 的createClient方法的源码中看到一些与创建 AOP 代理相似的代码,因此,我推测 Spring 之后可能会增加类似的注解来方便地创建代理对象。

其他特性

除了上述例子中的简单使用之外,添加了 HttpExchange 的方法还支持各种类型的参数,这一点也与 Spring MVC 的 Controller 方法类似,方法的返回值也可以是任意自定义的实体类型(就像上面的例子一样),此外,还支持自定义的异常处理。

为什么需要 Spring Reactive Web 的依赖

上文中创建工程的时候,引入了 Spring Reactive Web 的依赖,在创建代理的service对象的时候,使用了其中的 WebClient 类型。这是因为,HTTP Interface 目前只内置了 WebClient 的实现,它属于 Reactive Web 的范畴。Spring 在会在后续版本中推出基于 RestTemplate 的实现。

总结

本文带你对 HTTP Interface 特性进行了简单的了解,我之后会深入研究这个特性,也会追踪后续版本中的改进并与你分享。

分享好友

分享这个小栈给你的朋友们,一起进步吧。

Java小专栏
创建时间:2019-08-08 21:32:06
Java专栏专注技术分享
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • lihong
    栈主

小栈成员

查看更多
  • 栈栈
  • 杨三百
  • at_1
  • gaokeke123
戳我,来吐槽~