02、Feign原生注解

注解介绍

之前介绍过,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"})

可以看到,注解上标识的请求头被添加到了请求中。
&nbsp;

@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后面。
&nbsp;

@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 字符串,如果在接口提供方用对象是接受不到的。。。具体怎么回事,后续会介绍
&nbsp;