Skip to content

进阶教程

gudqs7 edited this page Oct 3, 2022 · 2 revisions

进阶使用

Spring MVC 注解支持表

注解 注解字段 是否支持 作用描述 备注
@RequestMapping value/path 支持 绑定一个或多个 url
@RequestMapping method 支持 限制请求的 HTTP 方法类型
@RequestMapping consumes 不支持 限制请求 Content-Type必须被此字段包含,否则抛出异常
@RequestMapping produces 不支持 限制请求 Accept 必须与此字段对应,否则抛出异常
@GetMapping 同 @RequestMapping 支持 @RequestMapping + 限制方法类型为 GET
@PostMapping 同 @RequestMapping 支持 @RequestMapping + 限制方法类型为 POST
@PutMapping 同 @RequestMapping 支持 @RequestMapping + 限制方法类型为 PUT
@DeleteMapping 同 @RequestMapping 支持 @RequestMapping + 限制方法类型为 DELETE
@RequestBody 支持 修饰的参数在一系列操作下可与HTTP 请求 Body 映射 插件读取后会将除此之外的参数拼接到 url 上,请求示例则展示为 json 格式
@RequestParam value/name 支持 覆盖参数变量名
@RequestParam required 支持 指定是否必填,默认为 true

注释 / Swagger 注解对应表

本插件支持读取注释 / 注解信息来生成文档,因为 Swagger 用户比较多,考虑到最小成本改造,注解采用的正是 Swagger 这一套,注释则是自己随便定义了一些 tag,具体见下表格!

注解 注解字段 对应注释 作用说明 备注
@Api tags #tags 或 @tags 接口分组名称 插件将此字段信息用于两处(批量生成时的按类分组生成的文档文件名/Postman 文件夹名称)
@Api description 不带注释 tag 部分连起来(可换行)或@description 接口分组描述 分组描述用不上,因此我将其作为 tags 的备用,无 tags 字段时,使用此字段。具体逻辑见代码 (cn.gudqs7.plugins.docer.pojo.annotation.CommentInfo#getItemName)
@Api hidden #hidden 或 @hidden 隐藏此类下所有接口 即跳过此类下所有接口,不生成(但类上直接右键时会忽略此字段,毕竟都指名道姓了,再忽略就不合理了!
@ApiOperation value 不带注释 tag 部分连起来(可换行) 接口名称(接口简述)
@ApiOperation notes #notes 或 @notes 接口详细描述
@ApiOperation hidden #hidden 或 @hidden 隐藏该接口 即跳过此接口,不生成(但方法上直接右键时会忽略此字段,毕竟都指名道姓了,再忽略就不合理了!
@ApiOperation tags 不支持 覆盖 Controller 上 @Api 的 tags 字段,将接口分组 暂不支持,个人无这个需求,目前分组逻辑是类下的为一组,放到同一个文件下,不同包下不同文件夹。(主要目前代码逻辑要支持改动挺大的)
@ApiOperation 其他字段 不支持
@ApiParam name 不支持 覆盖变量名 Swagger 本身无法对 MVC 规则变动,一般需搭配 @RequestParam,而本插件是支持 @RequestParam 的
@ApiParam value @param 后面非 tagKey=tagValue 部分 字段描述
@ApiParam required @param xxx required 字段是否必填 默认为 required=false,可写为 required=true,也可直接写为 required
@ApiParam example @param xxx example=yyy 字段示例值,字段默认值
@ApiParam hidden @param xxx hidden 隐藏改字段 写法同 required,凡是 bool 值均如此
@ApiModel value 类注释中非 tag 部分(可换行) 类的简述
@ApiModel description 不支持 类的描述 暂不支持,后续考虑加上吧,用处不大。
@ApiModelProperty value 字段注释中非 tag 部分(可换行)
@ApiModelProperty notes #notes 或 @notes
@ApiModelProperty example #example 或 @example
@ApiModelProperty required #required 或 @required 值为 true 或 false,由于默认值为 false,因此希望指定为 true 时,可省略值不填,即 @required == @required true(所有bool类型都如此)
@ApiModelProperty hidden #hidden 或 @hidden 同上
@ApiModelProperty name 不支持 覆盖字段变量名 暂不支持,考虑到目前没遇到前后的名称风格不一致的情况(前端无话语权),暂时就这样吧!

更多有用的注释/注解

虽然提到了注解,但更多时候,我都是增加注释来完善文档的,毕竟注解的侵入性较高,还需要增加依赖 jar。

注释

@ 或 #,其实都一样

可能你已经注意到了,上面的表格对应注释总是有两种写法,但他们其实只有前缀不同。
我在代码中解析时,两个符号都会判断,等同对待,因此任一种写法都是 OK 的。
之所以会加一个 # 开头,主要是考虑大部分同学并不知道(我也不知道)如何设置自定义注释 tag,以确保 IDEA 某条规则解析时不会以警告处理。而 #,反而不会触发这条报警规则!

注释还是注解? @important 说了算!

虽然你有注解,但我偏喜欢用注释来生成文档,此时需要使用此注解来告诉插件,使用注释而忽略注解!
之所以这么设计,是因为我虽然更偏爱注释,但也知道大部分注释都是不完整的,而注解则一般较为完整;因此设计优先级时,存在注解则优先使用注解的信息;
于是,我们需要一种方式来提高注释的优先级;此时我便想到了 CSS 的 !important,于是添加了这个注解。
该注释值为 true/false,一般写作 #important 即可。
该注释支持位置如下:类上,字段上,方法上,参数中(即 @param 这行,注意参数的注释以 xx=xx 格式,不同于其他三处写法)

接口并非成功或失败?有多种状态?试试 @code!

若您的接口有多个返回状态码,一般为错误码;
您可使用 @code 50001 错误信息xxxx 来描述这个状态,若有多个,则多写几行即可!

请看一个示例:

/**
 * 新增一个用户
 *
 * @param createUserRequest 用户信息
 * @return 操作是否成功
 * #hiddenResponse totalCount
 * #code 10001 用户昵称已存在
 * #code 10002 用户年龄范围无效, 即用户年龄需在0~200之间的数字
 */
@PostMapping("/createUser")
public BaseResponse<Boolean> createUser(CreateUserRequest createUserRequest) {
    return BaseResponse.success();
}

生成文档如下图,将多出一段 Code 含义信息表格
tag-code-doc.png

友情提示,该功能 Swagger 也支持,请翻到看下面注解篇查看!

某个入参字段或返回值字段不需要出现在这个接口,但会出现在那个接口,怎么办?

有时候,部分数据传输类被多个接口复用,但某个接口可能不需要某个字段(不论入参出参),此时我们可以使用 @hiddenRequest@hiddenResponse 来排除字段;
类似的,你也可以使用 @onlyRequest@onlyResponse 来指定仅需要的字段。
其中 request 后缀指代入参,response 后缀指代出参。
另外,排除和仅包含只能出现一个,否则排除优先级更高,另一个则不会起作用。
关于出参,用法比较确定,毕竟只有一个返回值,以此为根对象,指定字段非常方便;如上个示例中我就隐藏了 totalCount 这个分页相关的出参,其为返回类型下的一个字段;也可以使用 data.xxx 这种格式,通过 . 来指定多层结构下的字段。
而入参,我采用的方式是以 Spring MVC 的解析方式为准,即若所有参数以 application/x-www-form-urlencoded 方式绑定,则表单中的组件名称与变量名称对应;
且当参数为对象类型时,其下的字段与表单中组件名称一致,而不是带有参数的变量名为前缀;如上个示例中,createUserRequest 这个变量就是如此。
总计就是不带有参数变量名为前缀的写法。
这与 knife4j 定义的 @ApiOperationSupport 注解中 includeParameters,ignoreParameters 并不相同;
不过您不用担心,我在程序中已经做了处理,使用其注解时,按照 knife4j 的写法来即可,插件依然能正确识别。

使用 @json 或 @date 来指定日期时间格式

两个注释作用一样,@date 的优先级高;
此注释只能用于字段上。

示例:

/**
 * 开始时间(精度为天)
 * @json yyyy-MM-dd
 * @data yyyy-MM-dd
 */
private Date startTime;

生成的文档的结果如下:

{
  "startTime": "2022-05-29"
}

此注释依据 @JsonFormat@DateTimeFormat 注解字段 pattern 演化而来,因此使用注解也会识别,都存在时,注解优先级高!(@important 此时无效)

使用 @noResponse 来取消 Postman 请求下的示例生成。

如题,添加注释后,生成的 Postman 请求下,返回示例将不生成!
此注释可用于类上,方法上,方法上优先级高(就近原则)。

使用 @guid 和 @random 来控制示例值的生成

您可通过 @guid 来指定示例值为一个 GUID;
使用 @random 来指定字符串示例值的生成时随机的(有字段描述时,非随机,此时可用注释指定为随机,无字段描述时,默认随机);
这两个注释仅适用于字符串类型的字段;
此注释可用于,方法参数上,字段上。

注解

与 @code 对应的 @ApiResponse 和 @ApiResponses

  • @ApiResponsecodemessage 分别对应 Code 和 含义
  • @ApiResponses 则用于包含多个 @ApiResponse

与 @onlyRequest/@hiddenRequest 对应的 @ApiOperationSupport

  • 注意此注解仅 knife4j 拥有。
  • 上面有提到,knife4j 的写法是包含参数变量作为前缀的,和我注释的写法是不一样的,请不要混淆两者!
  • knife4j 仅支持入参,不支持出参,而我的注释是均支持,您可以在注解的基础上,使用注释来设置出参,这样是可以生效的。(这点适用于绝大多数更多注释)

与 @json/@date 对应的 @JsonFormat 和 @DateTimeFormat

  • @JsonFormatpattern 字段值与注释 @json 对应。
  • @DateTimeFormatpattern 字段值与注释 @date 对应。

Hibernate validator 的部分注解支持

  • @NotNull / @NotEmpty / @NotBlank:包含此三个任意一个注解的字段或参数,其是否必填会被设置为 true。
  • @Length:包含此注解时,其字段 max / min 指定了字符长度最大值和最小值,其他参考信息列会多出相应的描述。
  • @Range / @Min / @Max:指定了数字的范围,其他参考信息列会多出相应的描述。

以上的 Hibernate validator 注解均支持在参数上或字段上。

除文档生成外更多功能

导出到 Postman

直接在项目上右键(或某个目录/某个类/任意多选亦可),然后点击 Export Api Interface to Postman 的按钮,如下图
img.png
生成的结果文件在文档跟目录(一般为项目路径/api-doc)下 postman 目录下,有个 xxx.postman_collection.json,然后在 Postman 导入界面选择此文件即可。

导出到 Postman 有两种模式

  • 离线模式,生成文件到 postman 目录下。
  • 在线模式,通过配置文件指定 Postman 的 Api Key,可直接通过 api 生成到你的 workspace 下(但无法控制生成到哪个 workspace 下,Postman 提供的 Api 不够完善的问题。)

另外,当仅选择一个类右键时,会根据此类为标识来生成新的 Collection(默认以项目名或在配置文件中指定);
这样做的好处是,不会因为调试某个类把原来的 Collection 覆盖掉。

使用 Search Everywhere 根据 url 搜索并快速跳转到接口定义

双击 Shift 进入 Search Everywhere 后切换到 Api,或使用快捷键 Ctrl + \ Ctrl + Alt + N 进入如下图界面。
此时您可通过 url 或接口描述来搜索并跳转到该接口。

img.png

使用智能上下文或后缀快速生成实体类的所有 set 方法 / get 方法

以下展示后缀的使用方法,但使用智能上下文得到的效果是类似的,只是个人认为后缀效率更高一丢丢!

下面演示所使用的基础类

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Foo {

    private Integer testInt;

    private Long testLong;

    private Float testFloat;

    private Double testDouble;

    private Boolean testBoolean;

}

生成 set

public void usage01() {
    // 用法1, 生成所有 set 方法, 带默认值, 通过 postfix
    Foo foo = new Foo();
    // 取消下面的注释, 光标位于 allset 后面, 按下 Tab 键
//        foo.allset
    // 即可得到下面结果, 且 foo.allset 会自动消失
    foo.setTestInt(0);
    foo.setTestLong(0L);
    foo.setTestFloat(0f);
    foo.setTestDouble(0D);
    foo.setTestBoolean(false);
}

生成 get

public void usage03() {
    // 用法3, 生成所有 get 方法, 通过 postfix
    Foo foo = new Foo();
    // 取消下面的注释, 光标位于 allget 后面, 按下 Tab 键
//        foo.allget
    // 即可得到下面结果, 且 foo.allget 会自动消失
    Integer testInt = foo.getTestInt();
    Long testLong = foo.getTestLong();
    Float testFloat = foo.getTestFloat();
    Double testDouble = foo.getTestDouble();
    Boolean testBoolean = foo.getTestBoolean();
}

Lombok 的 @Builder 快速生成所有赋值方法的调用链

public void usage04() {
    // 用法4, 生成所有 set 方法, 通过 builder, 通过 postfix
    // 取消下面的注释, 光标位于 allbuilder 后面, 按下 Tab 键
//        Foo.builder().allbuilder
    // 即可得到下面结果
    Foo foo = Foo.builder()
            .testInt(0)
            .testLong(0L)
            .testFloat(0f)
            .testDouble(0D)
            .testBoolean(false)
            .build();
}

根据一段含有源对象(a)/目标对象(b)的 b.setXxx(a.getXxx()) 方法代码生成所有 set 方法以快速实现对象转换

public void usage05() {
    // 用法5, 将 src 的数据赋值给 dest, 常用于两个不同类直接进行 convert(需字段名称相同), 通过 postfix
    Foo src = new Foo();
    Foo dest = new Foo();
    // 取消下面的注释, 光标位于 convert 后面, 按下 Tab 键
//        dest.setTestInt(src.getTestInt());.convert
    // 即可得到下面结果
    dest.setTestInt(src.getTestInt());
    dest.setTestLong(src.getTestLong());
    dest.setTestFloat(src.getTestFloat());
    dest.setTestDouble(src.getTestDouble());
    dest.setTestBoolean(src.getTestBoolean());
}

根据一个入参为源对象(a),返回值为目标对象(b)的方法声明,生成对象转换方法

public Foo usage09(Foo src) {
    // 用法9 通过在方法名称上触发 Show Context Actions, 快捷键(如 Alt + Enter 或右键菜单选择)
    //  可看到 Generate Convert,选中后生成下面注释里的内容(不包含 return null;)
//        Foo foo = new Foo();
//        foo.setTestInt(src.getTestInt());
//        foo.setTestLong(src.getTestLong());
//        foo.setTestFloat(src.getTestFloat());
//        foo.setTestDouble(src.getTestDouble());
//        foo.setTestBoolean(src.getTestBoolean());
//        return foo;
    return null;
}

上面这些代码在示例代码块cn.gudqs.example.genset.GenerateSetterAndGetterUsage 下。
欢迎 Clone 体验。