Feign详解1 ╰+哭是因爲堅強的太久メ 2022-11-21 04:10 187阅读 0赞 **目录** 1. 什么是 Feign 2. Feign 解决了什么问题 1). 引入 maven 依赖 2). 定义查询请求中的参数的封装类 3). 定义接口 4). 测试类 3. Feign 声明式注解 -------------------- ## 1. 什么是 Feign ## `Feign` 的英文表意为“假装,伪装,变形”, 是一个 Http 请求调用的轻量级框架,可以以 Java 接口注解的方式调用 Http 请求,而不用像 Java 中通过封装 HTTP 请求报文的方式直接调用。 `Feign` 通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。 `Feign` 被广泛应用在 Spring Cloud 的解决方案中,是学习基于 Spring Cloud 微服务架构不可或缺的重要组件。 `Feign` 开源项目地址:[https://github.com/OpenFeign/feign][https_github.com_OpenFeign_feign] ## 2. Feign 解决了什么问题 ## > **Feign 封装 HTTP 调用流程,面向接口编程。** > > **Feign 本身很简单,但做了大量的适配工作,这也是这个框架存在的意义。** 在服务调用的场景中,我们经常调用基于 Http 协议的服务,而我们经常使用到的框架可能有HttpURLConnection、Apache HttpComponnets、OkHttp3 、Netty 等等,这些框架在基于自身的专注点提供了自身特性。而从角色划分上来看,他们的职能是一致的提供 Http 调用服务。具体流程如下: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70][] **总结:** 根据上图的分析可知:要实现 `Feign` 客户端,主要是将 Method 方法的参数解析成 Http 请求的请求行、请求行、请求体,然后使用 HttpClient 发送请求。但为了实现这些设想,要解决以下问题: * REST 声明式规范有以下几种:Feign、JAX-RS 1/2、Spring Web MVC 都需要进行适配。这几种声明式注解的适配接口是 `feign.Contract`。 * Http 客户端有 JDK 自带的 HttpURLConnection、Apache HttpComponnets、OkHttp3 、Netty 等,需要适配。这几种客户端的适配接口是 `feign.Client`。 * 支持负载均衡与熔断。负载均衡 Ribbon 是对 `feign.Client` 进行包装。熔断 Hystrix 在 HystrixFeign 中自定义 InvocationHandlerFactory 的实现,创建 HystrixInvocationHandler,对 method.invoke 请求做拦截。 * 各种编解码的适配,实现接口 `feign.codec.Decoder` 和 `feign.codec.Encoder`。 下面我们先跟着官网的示例来做一个入门案例,这个案例的功能是访问github中的openfeign的contributor信息. github中的API列表如下: [https://api.github.com/][https_api.github.com] ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70 1][] 我们将访问: repository\_url: "[https://api.github.com/repos/\{owner\}/\{repo\}][https_api.github.com_repos_owner_repo] 这个接口, 它里面要加入两个参数 owner, repo, 构成访问: [https://api.github.com/repos/OpenFeign/feign/contributors][https_api.github.com_repos_OpenFeign_feign_contributors]. 访问后的结果页: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70 2][] ### 1). 引入 maven 依赖 ### <dependencies> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> <version>10.4.0</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-gson</artifactId> <version>10.4.0</version> </dependency> </dependencies> ### 2). 定义查询请求中的参数的封装类 ### //两个参数, 用于 请求的地址: https://api.github.com/repos/OpenFeign/feign/contributors 返回值的封装 // public class Contributor { String login; int contributions; int id; String node_id; String avatar_url; String gravatar_id; String url; String html_url; String followers_url; // String following_url; // String gists_url; // String starred_url; // String subscriptions_url; // String organizations_url; // String repos_url; // String events_url; // String received_events_url; // String type; @Override public String toString() { return "Contributor{" + "login='" + login + '\'' + ", contributions=" + contributions + ", id=" + id + ", node_id='" + node_id + '\'' + ", avatar_url='" + avatar_url + '\'' + ", gravatar_id='" + gravatar_id + '\'' + ", url='" + url + '\'' + ", html_url='" + html_url + '\'' + ", followers_url='" + followers_url + '\'' + '}'; } } 这个类对应 请求地址: [https://api.github.com/repos/OpenFeign/feign/contributors][https_api.github.com_repos_OpenFeign_feign_contributors] 的返回结果的封装 ### 3). 定义接口 ### public interface GitHub { // 传入的参数后可以拼接成url: https://api.github.com/repos/OpenFeign/feign/contributors @RequestLine("GET /repos/{owner}/{repo}/contributors") List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); } ### 4). 测试类 ### public class MyApp { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); List<Contributor> contributors = github.contributors("OpenFeign", "feign"); for (Contributor contributor : contributors) { System.out.println(contributor); } } } 小结: **`Feign.target()` **实际上是创建了一个 GitHub 的动态代理。通过这个代理来访问url,获取到结果后,包装成对象返回. ## 3. Feign 声明式注解 ## Feign 通过 `Contract` 接口将方法上标注的注解解析成 MethodMetadata,最终将参数解析成 Http 请求的请求行、请求行、请求体,然后使用 HttpClient 发送请求。 <table> <thead> <tr> <th>Annotation</th> <th>Interface Target</th> <th>Usage</th> </tr> </thead> <tbody> <tr> <td><code>@RequestLine</code></td> <td>Method</td> <td>定义HttpMethod 和 UriTemplate. UriTemplate 中使用<code>{}</code> 包裹的表达式,可以通过在方法参数上使用@Param 自动注入</td> </tr> <tr> <td><code>@Param</code></td> <td>Parameter</td> <td>定义模板变量,模板变量的值可以使用名称的方式使用模板注入解析</td> </tr> <tr> <td><code>@Headers</code></td> <td>Method, Type</td> <td>定义头部模板变量,使用@Param 注解提供参数值的注入。如果该注解添加在接口类上,则所有的请求都会携带对应的Header信息;如果在方法上,则只会添加到对应的方法请求上</td> </tr> <tr> <td><code>@QueryMap</code></td> <td>Parameter</td> <td>定义一个Map或 POJO,参数值将会被转换成URL上的 query 字符串上</td> </tr> <tr> <td><code>@HeaderMap</code></td> <td>Parameter</td> <td><code>Map</code> -><code>Http Headers</code></td> </tr> <tr> <td><code>@Body</code></td> <td>Method</td> <td>Defines a <code>Template</code>, similar to a <code>UriTemplate</code> and <code>HeaderTemplate</code>, that uses <code>@Param</code> annotated values to resolve the corresponding <code>Expressions</code>.</td> </tr> </tbody> </table> 以上注解的基本使用案例: public interface FeignService { // @Headers @RequestLine("GET /api/documents/{contentType}") @Headers("Accept: {contentType}") String getDocumentByType(@Param("contentType") String type); // @QueryMap: Map or POJO @RequestLine("GET /find") V find(@QueryMap Map<String, Object> queryMap); @RequestLine("GET /find") V find(@QueryMap CustomPojo customPojo); // @HeaderMap: Map @RequestLine("POST /") void post(@HeaderMap Map<String, Object> headerMap); // @Body @RequestLine("POST /") @Headers("Content-Type: application/xml") @Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>") void xml(@Param("user_name") String user, @Param("password") String password); @RequestLine("POST /") @Headers("Content-Type: application/json") @Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D") void json(@Param("user_name") String user, @Param("password") String password); } [https_github.com_OpenFeign_feign]: https://github.com/OpenFeign/feign [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70]: /images/20221120/2f19e40d2f71435a9d6cb694ae3f64e1.png [https_api.github.com]: https://api.github.com/ [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70 1]: /images/20221120/b4b60e7ffedb46e9b3a0f66cd53186a9.png [https_api.github.com_repos_owner_repo]: https://api.github.com/repos/%7Bowner%7D/%7Brepo%7D [https_api.github.com_repos_OpenFeign_feign_contributors]: https://api.github.com/repos/OpenFeign/feign/contributors [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poYW5neWluZ2NoZW5ncWk_size_16_color_FFFFFF_t_70 2]: /images/20221120/9cc6a5f7efbf4064b757bac272e20241.png
还没有评论,来说两句吧...