注解介绍
之前介绍过,Feign 是通过接口+注解的方式声明一个HTTP 请求。
Feign 注解定义了接口和底层客户端应之间该如何工作的关系。Feign 的默认定义了以下注解:
注解 | 作用位置 | 用法 |
---|---|---|
@RequestLine | 方法 | 为请求定义HttpMethod和UriTemplate。 花括号{expression}中的值使用其相应的带@Param注解的参数解析。 |
@Param | 参数 | 定义一个模板变量,其值将用于解析相应的表达式模板,通过作为注解值提供的名称。如果缺少值,它将尝试从字节码方法参数名称中获取名称(如果代码是用-parameters标志编译的)。 |
@Headers | 方法、类型 | 定义一个HeaderTemplate。使用带@Param注释的值来解析相应的Expressions. 在 Type上使用时,模板将应用于每个请求。 |
@QueryMap | 参数 | 范围 定义一个Map名称-值对或 POJO,以扩展为查询字符串。 |
@HeaderMap | 参数 | 范围 定义一个Map名称-值对,扩展为Http Headers |
@Body | 方法 | 定义 Template,类似于UriTemplateand、 HeaderTemplate,它使用带@Param注释的值来解析相应的Expressions. |
使用案例
@RequestLine
概述
RequestLine
是请求行的意思,我们知道在HTTP 中,请求行一般由以下几部分构成。
@RequestLine
注解只能标注在方法上,为请求定义HttpMethod(请求方式 GET、POST等)和UriTemplate(访问路径),然后可以将@Param
注解中表示的参数,解析到表达式{ }对应的位置上。
@RequestLine
注解有以下几个配置项,一般我们只需要配置value值就可以了。
@Target({
ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLine {
// 请求行的值
String value();
// 斜杠转义,默认true
boolean decodeSlash() default true;
// URL参数中编码方式,默认使用 &aa=aabb&bb=bb,不需要改
CollectionFormat collectionFormat() default CollectionFormat.EXPLODED;
}
案例
接下来我们看下如何使用@RequestLine
注解进行传参访问。
首先将接口改为需要一个name参数:
@GetMapping("/test")
@ApiOperation(value = "测试接口")
@ApiOperationSupport(author = "xiaoymin@foxmail.com")
public String test(String name) {
return name;
}
Feign 接口中,定义好请求行,使用@Param
注解表示将请求参数赋值给表达式{name}。
public interface TestFeignClient {
@RequestLine("GET /app1/test?name={name}")
String test(@Param("name") String name);
}
然后创建客户端并远程访问:
public static void main(String[] args) {
// 1. 生成Feign 客户端
TestFeignClient feignClient = Feign.builder()
.logger(new Logger.ErrorLogger()).logLevel(Logger.Level.FULL) // 打印所有日志
.retryer(Retryer.NEVER_RETRY) // 关闭重启
.target(TestFeignClient.class, "http://127.0.0.1:9001"); // 接口及请求地址
// 2. 打印结果
String test = feignClient.test("张三");
System.out.println(test);
}
启动该工程,并执行Feign 请求,可以通过日志,看到当前请求的整个过程。
@Param
概述
@Param
只能做用于方法参数上,它的主要作用是,通过变量名,绑定一个参数值,然后可以将参数填充到模板表达式中。
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
public @interface Param {
// Key,对应模板表达式中的变量
String value() default "";
// 填充方式,默认直接使用ToString方法,
Class<? extends Param.Expander> expander() default Param.ToStringExpander.class;
// 是否转义,默认false
/** @deprecated */
boolean encoded() default false;
// 内部类填充器 使用ToString方法填充
public static final class ToStringExpander implements Param.Expander {
public ToStringExpander() {
}
public String expand(Object value) {
return value.toString();
}
}
public interface Expander {
String expand(Object var1);
}
}
在上面的案例中,会将name参数,填充到{name} 表达式中。
@RequestLine("GET /app1/test?name={name}")
String test(@Param("name") String name);
@Headers
概述
@Headers
可作用于方法或者类上,用于添加请求头。
它的属性很简单,是一个字符串数组。
@Target({
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Headers {
// 请求头
String[] value();
}
案例
注意添加的时候,消息头键值对使用冒号:分隔。
@Headers({
"Accept:*/*", "Accept-Language:zh-cn"})
可以看到,注解上标识的请求头被添加到了请求中。
@QueryMap
概述
@QueryMap
注解只可作用于方法参数上,表示将多个参数拼接在URL后面访问。
其只有一个属性,表示是否转义,默认false。
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.PARAMETER})
public @interface QueryMap {
boolean encoded() default false;
}
案例
如果某个请求接口需要多个参数,这个时候就可以使用@QueryMap
注解传递,注意这个会后参数必须是Map类型的。
比如我们可以通过以下方式传递:
@RequestLine("GET /app1/test")
@Headers({
"Accept:*/*", "Accept-Language:zh-cn"})
String testQueryMap(@QueryMap Map<String, String> map);
可以看到在请求时,将Map中的键值对拼接到了URL后面。
@HeaderMap
概述
@HeaderMap
注解只能做用于方法参数上,和@QueryMap
差不多,是将Map中的键值对,添加到请求头中。
该注解没有配置项:
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.PARAMETER})
public @interface HeaderMap {
}
案例
@RequestLine("GET /app1/test")
String testQueryMap(@HeaderMap Map<String, String> map);
@Body
概述
@Body
做用于方法上,会使用请求体来传递参数。可以传递字符串、Json数据,在传递Bean 对象时,会调用其toString
方法再进行传递。
该注解只有一个属性值,可以写表达式,通过@Param
传值。
@Target({
ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Body {
String value();
}
案例
通过请求体传递一个实体类对象。
@RequestLine("POST /app1/post")
@Body("{user}")
String post(@Param("user") User user);
可以看到在发送请求时,实际传递的是实体类的toString 字符串,如果在接口提供方用对象是接受不到的。。。具体怎么回事,后续会介绍