《 Feign 基本理论概述 》
前言
本篇文章主要对 Open Feign 进行了基本的介绍,包括什么是 Feign ?Feign 的作用?Feign 与 JDK 版本的兼容性?以及Feign 常用注解的说明和使用示例?
Feign 基本理论概述
1、关于 Feign
Feign 是一个 Java 到 HTTP 的客户端绑定器,其灵感来自于 Retrofit、JAXRS-2.0 和 WebSocket。Feign 的第一个目标是降低将其公共特性统一绑定到 HTTP API 的复杂性,而不考虑 ReSTfulness。
Feign 是一个声明式 “ Web Service Client ”。使用 Feign 能让编写 Web Service Client 更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持 JAX-RS 标准的注解。Feign 也支持可拔插式的编码器和解码器。Spring Cloud 对 Feign 进行了再封装,使其支持了Spring MVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。
2、Feign 的作用
Feign 旨在使编写 Java Http Client 变得更容易。在上一个微服务实例中使用了 Ribbon + RestTemplate 实现 Java Http Client,利用 RestTemplate 对 http 请求进行了再封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign 在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在 Feign 的实现下,只需创建一个接口并使用注解(Feign 注解)的方式来配置它(以前是在 Dao 接口上面标注 Mapper 注解,现在只需要在一个微服务接口上面标注一个 Feign 注解即可),即可完成对服务提供方的接口绑定,简化了使用 Spring cloud Ribbon 时,自动封装服务调用客户端的开发量,提高了开发效率。
3、Feign 与 JDK 版本的兼容性
Feign-10.x 及以上版本是基于 JDK-1.8 构建的,可以在 JDK-1.8、JDK-1.9、JDK-10以及JDK-11 上运行。 对于那些需要 JDK 6兼容性,请使用 Feign-9.x。
4、Feign 常用注解说明
注解 | 注解目标 | 使用说明 |
@Headers |
方法/类 |
定义了一个 HeaderTemplate,该注解由 UriTemplate 演变而来。它使用 @Param 注释值来解析相应的表达式。当在 Class 上使用时,template 将应用于每个请求。当在某个 Method 上使用时,template 将只应用于带该注释的方法上。 |
@RequestLine | 方法 | 为请求定义 HttpMethod 和 UriTemplate 表达式,用大括号 {表达式} 包装的值使用它们对应的 @Param注释参数解析。 |
@Body | 方法 | 定义一个模板,类似于 UriTemplate 和 HeaderTemplate,它使用 @Param 注释值来解析相应的表达式。 |
@Param | 参数 | 定义模板变量,其值将用于按名称解析相应的模板表达式。 |
@QueryMap | 参数 | 定义名称-值的对应关系,或 POJO,以展开为查询字符串。 |
@HeaderMap | 参数 | 定义名称-值的对应关系,以展开到 Http Header 中。 |
注解使用示例(只列举部分内容):
1)、模板和表达式
Feign 表达式表示由 URI Template-RFC 6570 定义的简单字符串表达式。表达式使用相应的带注释的方法参数进行扩展。例如:
接口:
/**
*
* @author HuaZai
* @contact [email protected]
* <ul>
* @description TODO
* </ul>
* @className API
* @createdTime 2017年5月28日 下午2:03:43
*
* @version V1.0.0
*/
public interface API
{
@RequestLine("GET /repos/{owner}/{repo}/emp")
List<Emp> getEmps(@Param("owner") String owner, @Param("repo") String repository);
class Emp
{
String login;
int contributions;
}
}
main App:
/**
*
* @author HuaZai
* @contact [email protected]
* <ul>
* @description TODO
* </ul>
* @className Etcp_86575_App
* @createdTime 2017年5月28日 下午2:15:10
*
* @version V1.0.0
*/
public class Etcp_86575_App
{
public static void main(String[] args)
{
Emp emp = Feign.builder().decoder(new GsonDecoder()).target(Emp.class, "https://www.68521eureka.com");
/*
* owner 和 repository 参数将用于展开 RequestLine 中定义的 owner 和 repo 表达式。
*
*
* 得到的uri将是 https://www.68521eureka.com/repos/OpenFeign/feign/emp
*/
Emp.contributors("OpenFeign", "feign");
}
}
2)、JAX-RS
JAXRSContract 重写了注解处理,以使用 JAX-RS 规范提供的标准注解处理。这是目前针对 1.1 的规范,下面是使用 JAX-RS 重新编写的一个简单的例子:
/**
*
* @author HuaZai
* @contact [email protected]
* <ul>
* @description TODO
* </ul>
* @className API
* @createdTime 2017年5月28日 下午6:41:44
*
* @version V1.0.0
*/
public interface API
{
@GET
@Path("/repos/{owner}/{repo}/emp")
List<Emp> contributors(@PathParam("owner") String owner, @PathParam("repo") String repo);
}
public class Example
{
public static void main(String[] args)
{
Emp emp = Feign.builder().contract(new JAXRSContract()).target(Emp.class, "https://www.68521eureka.com");
}
}
3)、@Body 模板
@Body 注解表示了一个使用 @Param 注解的参数扩展的模板。使用时可能还需要添加一个 Content-Type 的标题,示例代码如下:
/**
*
* @author HuaZai
* @contact [email protected]
* <ul>
* @description TODO
* </ul>
* @className LoginClient
* @createdTime 2017年5月29日 下午3:44:13
*
* @version V1.0.0
*/
public interface LoginClient
{
@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")
// json 花括号必须转义
@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
void json(@Param("user_name") String user, @Param("password") String password);
}
public class Example
{
public static void main(String[] args)
{
// <login "user_name"="denominator" "password"="secret"/>
client.xml("denominator", "secret");
// {"user_name": "denominator", "password": "secret"}
client.json("denominator", "secret");
}
}
4)、@Headers
根据不同的用例,Feign 支持将请求的设置标题作为 API 的一部分,或者作为客户端的一部分,在特定接口或调用应该始终设置某些头值的情况下,将头定义为 API 的一部分是有意义的,静态头部可以使用 @Headers 注释在 API 接口或方法上进行设置,示例代码如下:
/**
*
* @author HuaZai
* @contact [email protected]
* <ul>
* @description TODO
* </ul>
* @className BaseApi
* @createdTime 2017年5月29日 下午3:14:35
*
* @version V1.0.0
*/
@Headers("Accept: application/json")
public interface BaseApi<T>
{
@Headers("Content-Type: application/json")
@RequestLine("PUT /api/{key}")
void put(@Param("key") String key, T value);
}
在方法上可以使用 @Headers 中的变量扩展为静态 header 指定动态内容,示例代码如下:
/**
*
* @author HuaZai
* @contact [email protected]
* <ul>
* @description TODO
* </ul>
* @className Api
* @createdTime 2017年5月29日 下午3:20:34
*
* @version V1.0.0
*/
public interface Api
{
@RequestLine("POST /")
@Headers("X-Ping: {token}")
public void post(@Param("token") String token);
}
如果请求头字段键和值都是动态的,而且不能提前知道键的取值范围,并且在同一个 api/ 客户端中的不同方法调用之间可能会有所不同(例如,自定义元数据头字段,如 " x-amz-meta-* "或" x-goog-meta-* ") ,则可以使用 @HeaderMap 注释 Map 参数来构造一个查询,该查询使用 Map 的内容作为请求头参数,示例代码如下:
/**
*
* @author HuaZai
* @contact [email protected]
* <ul>
* @description TODO
* </ul>
* @className Api
* @createdTime 2017年5月28日 下午3:39:39
*
* @version V1.0.0
*/
public interface Api
{
@RequestLine("POST /")
void post(@HeaderMap Map<String, Object> headerMap);
}
这些方法将 header 的请求入口指定为 API 的一部分,并且在构建 Feign Client 时不需要任何定制即可。
3、Feign 与 Ribbon
通过 Ribbon 维护远程提供者服务列表信息,并且通过轮询实现了客户端的负载均衡。而 Feign 与 Ribbon 不同的是,通过 Feign 只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务的远程调用。
4、Feign 源码地址
《 OpenFeign 》
好了,关于 Spring Cloud 进阶--Feign 基本理论概述 就写到这儿了,如果还有什么疑问或遇到什么问题欢迎扫码提问,也可以给我留言哦,我会一一详细的解答的。
歇后语:“ 共同学习,共同进步 ”,也希望大家多多关注CSND的IT社区。
作 者: | 华 仔 |
联系作者: | [email protected] |
来 源: | CSDN (Chinese Software Developer Network) |
原 文: | https://blog.csdn.net/Hello_World_QWP/article/details/86476825 |
版权声明: | 本文为博主原创文章,请在转载时务必注明博文出处! |