在 Netflix Feign 中,@Param 注解主要用于将方法参数绑定到请求的参数中。它允许你在定义 Feign 客户端接口方法时,明确指定方法参数如何映射到请求的 URL 参数、请求头或者请求体中,使得远程服务调用能够正确地传递参数信息。
注意:@Param 可以与 @RequestLine、@Headers 和 @Body 注解配合使用。
通过 @Param 定义参数,然后将参数绑定到请求查询参数,例如:
(1)服务代码:
@GetMapping("/param1") public String param1(@RequestParam("token") String token) { return "token=" + token; }
(2)Feign 客户端代码:
@RequestLine("GET /simple/param1?token={token}") String param1(@Param("token") String token);
(3)调用 Feign 客户端代码:
@GetMapping("/") public String index() { return SimpleFeign.create().param1("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); }
执行结果:
上述示例中,@RequestLine("GET /simple/param1?token={token}") 定义了一个 GET 请求,其中 {token} 是一个变量。@Param("token") String token 表示方法参数 token 将被绑定到路径中的 {token} 变量上(可以理解为 {token} 引用了 @Param("token") 定义的参数)。
当调用 param1 方法并传入一个 token(例如 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)时,Netflix Feign 会构建一个类似 /simple/param1?token=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 的请求路径发送到目标服务。
如果你想将一个参数绑定到请求头中,可以结合 @RequestHeader(Feign 中的另一个注解,后续将详细介绍)来实现。假设你需要传递一个认证令牌(token)到请求头中,例如:
(1)服务代码:
@GetMapping("/param2") public String param2(@RequestHeader("Authorization") String token) { return "Authorization=" + token; }
(2)Feign 客户端代码:
@RequestLine("GET /simple/param2") @Headers("Authorization: Bearer {token}") String param2(@Param("token") String token);
(3)调用 Feign 客户端代码:
@GetMapping("/") public String index() { return SimpleFeign.create().param2("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); }
执行结果:
上面代码中,@Headers("Authorization: Bearer {token}") 表示引用方法参数 token,将 token 的值以Authorization 这个请求头的名称进行传递。例如,如果 token 的值为 Bearer 123456,Feign 会在请求头中添加 Authorization: Bearer 123456,然后发送 GET 请求到 /simple/param2 端点。
如果你想将一个参数绑定到请求体中,可以结合 @Body 注解(Feign 中的另一个注解,后续将详细介绍)来实现。假设你需要通过请求体传递一段文本(注意:传递请求体请使用 POST 请求),例如:
(1)服务代码:
@PostMapping("/param3") public String param3(@RequestBody String body) { return "body=" + body; }
(2)Feign 客户端代码:
@RequestLine("POST /simple/param3") @Body("{body}") String param3(@Param("body") String body);
(3)调用 Feign 客户端代码:
@GetMapping("/") public String index() { return SimpleFeign.create().param3("hello! body"); }
执行结果:
上面代码中,@Body("{body}") 表示引用方法参数 body,将 body 的值以请求体的方式进行传递。
下面是 @Param 注解的源码:
package feign; import java.lang.annotation.Retention; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * A named template parameter applied to {@link Headers}, {@linkplain RequestLine}, * {@linkplain Body}, POJO fields or beans properties when it expanding * 用于 @Headers、@RequestLine、@Body,POJO 字段或 Bean 属性的已命名模板参数。 */ @Retention(RUNTIME) @java.lang.annotation.Target({PARAMETER, FIELD, METHOD}) public @interface Param { /** * The name of the template parameter. * 模板参数的名称。 */ String value() default ""; /** * How to expand the value of this parameter, if {@link ToStringExpander} isn't adequate. * 如果 ToStringExpander 不满足要求,你可以自定义 Expander 扩展器。 */ Class<? extends Expander> expander() default ToStringExpander.class; /** * {@code encoded} has been maintained for backward compatibility and should be deprecated. We no * longer need it as values that are already pct-encoded should be identified during expansion and * passed through without any changes * encoded 是为了向后兼容而保留的,应予废弃。我们不再需要它,因为已被 pct-encoded 的 * 值应在扩展过程中识别出来,并在不做任何更改的情况下通过 * * @see QueryMap#encoded * @deprecated 已过期,不建议使用 */ boolean encoded() default false; interface Expander { /** * Expands the value into a string. Does not accept or return null. * 将数值扩展为字符串。不接受或返回空值。 */ String expand(Object value); } // 默认实现,将对象转为字符串 final class ToStringExpander implements Expander { @Override public String expand(Object value) { return value.toString(); } } }
下面演示如何自定义 Expander,该 Expander 将在值的前后添加一些固定字符串。Expander 代码如下:
class MyExpander implements Param.Expander { @Override public String expand(Object value) { return "123" + value + "321"; } }
在 Feign 中,使用自定义的 Expander,如下:
@RequestLine("POST /simple/param3") @Body("{body}") String param4(@Param(value = "body", expander = MyExpander.class) String body);
调用 param4() 方法,输出结果如下: