spring boot:用swagger3生成接口文档,支持全局通用参数(swagger 3.0.0 / spring boot 2.3.2)

一,什么是 swagger?

1,  Swagger 是一个规范和完整的文档框架,

    用于生成、描述、调用和可视化 RESTful 风格的 Web 服务文档

    官方网站:

https://swagger.io/

 

2,使用 swagger 要注意的地方:

     在生产环境中必须关闭 swagger,

     它本身只用于前后端工程师之间的沟通,

     可以专门使用一台内部服务器来展示 ui 供访问,

     即使在这上面要做好安全措施

 

3,  因为 swagger3.0.0 已发布,本文使用了最新版

     如果有还在用 2.x 版本的请参考时注意区分

 

说明:刘宏缔的架构森林是一个专注架构的博客,

网站:https://blog.imgtouch.com
本文: https://blog.imgtouch.com/index.php/2023/05/24/springboot-yong-swagger3-sheng-cheng-jie-kou-wen-dang-zhi-chi-quan-ju-tong-yong-can-shu/

         对应的源码可以访问这里获取: https://github.com/liuhongdi/

说明:作者: 刘宏缔 邮箱: 371125307@qq.com

 

二,演示项目的相关信息

1, 项目地址

https://github.com/liuhongdi/swagger3

 

2, 项目功能说明

  演示了使用 swagger3.0.0 生成接口站的文档,

  包括:通用的全局参数,

           响应时各种状态的返回

           响应的封装后的 result 格式数据

           

3, 项目结构: 如图:

 

 

 

三,配置文件说明

1,pom.xml

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>

说明:用 springfox-boot-starter 来引入 swagger

 

2,application.properties

springfox.documentation.swagger-ui.enabled=true

说明:在生产环境中要设置 swagger-ui 的 enabled 值为 false,

用来关闭文档的显示

 

四,java 文件说明:

1,Swagger3Config.java

@EnableOpenApi
@Configuration
public class Swagger3Config implements WebMvcConfigurer {
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Docket createRestApi() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回文档摘要信息</span>
    <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Docket(DocumentationType.OAS_30)
            .apiInfo(apiInfo())
            .select()
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))</span>
            .apis(RequestHandlerSelectors.withMethodAnnotation(Operation.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">))
            .paths(PathSelectors.any())
            .build()
            .globalRequestParameters(getGlobalRequestParameters())
            .globalResponses(HttpMethod.GET, getGlobalResonseMessage())
            .globalResponses(HttpMethod.POST, getGlobalResonseMessage());
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成接口信息,包括标题、联系人等</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> ApiInfo apiInfo() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ApiInfoBuilder()
            .title(</span>"Swagger3接口文档"<span style="color: rgba(0, 0, 0, 1)">)
            .description(</span>"如有疑问,请联系开发工程师老刘。"<span style="color: rgba(0, 0, 0, 1)">)
            .contact(</span><span style="color: rgba(0, 0, 255, 1)">new</span> Contact("刘宏缔", "https://www.cnblogs.com/architectforest/", "371125307@qq.com"<span style="color: rgba(0, 0, 0, 1)">))
            .version(</span>"1.0"<span style="color: rgba(0, 0, 0, 1)">)
            .build();
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成全局通用参数</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> List&lt;RequestParameter&gt;<span style="color: rgba(0, 0, 0, 1)"> getGlobalRequestParameters() {
    List</span>&lt;RequestParameter&gt; parameters = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    parameters.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RequestParameterBuilder()
            .name(</span>"appid"<span style="color: rgba(0, 0, 0, 1)">)
            .description(</span>"平台id"<span style="color: rgba(0, 0, 0, 1)">)
            .required(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
            .in(ParameterType.QUERY)
            .query(q </span>-&gt; q.model(m -&gt;<span style="color: rgba(0, 0, 0, 1)"> m.scalarModel(ScalarType.STRING)))
            .required(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
            .build());
    parameters.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RequestParameterBuilder()
            .name(</span>"udid"<span style="color: rgba(0, 0, 0, 1)">)
            .description(</span>"设备的唯一id"<span style="color: rgba(0, 0, 0, 1)">)
            .required(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
            .in(ParameterType.QUERY)
            .query(q </span>-&gt; q.model(m -&gt;<span style="color: rgba(0, 0, 0, 1)"> m.scalarModel(ScalarType.STRING)))
            .required(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
            .build());
    parameters.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RequestParameterBuilder()
            .name(</span>"version"<span style="color: rgba(0, 0, 0, 1)">)
            .description(</span>"客户端的版本号"<span style="color: rgba(0, 0, 0, 1)">)
            .required(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
            .in(ParameterType.QUERY)
            .query(q </span>-&gt; q.model(m -&gt;<span style="color: rgba(0, 0, 0, 1)"> m.scalarModel(ScalarType.STRING)))
            .required(</span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
            .build());
     </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> parameters;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">生成通用响应信息</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> List&lt;Response&gt;<span style="color: rgba(0, 0, 0, 1)"> getGlobalResonseMessage() {
    List</span>&lt;Response&gt; responseList = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    responseList.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span> ResponseBuilder().code("404").description("找不到资源"<span style="color: rgba(0, 0, 0, 1)">).build());
     </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> responseList;
}

}

说明:生成了全局的参数和通用的响应信息

 

2,RestResult.java

@ApiModel("api 通用返回数据")
public class RestResult<T> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">uuid,用作唯一标识符,供序列化和反序列化时检测是否一致</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">long</span> serialVersionUID = 7498483649536881777L<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">标识代码,0表示成功,非0表示出错</span>
@ApiModelProperty("标识代码,0表示成功,非0表示出错"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Integer code;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">提示信息,通常供报错时使用</span>
@ApiModelProperty("提示信息,供报错时使用"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String msg;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常返回时返回的数据</span>
@ApiModelProperty("返回的数据"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> T data;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">constructor</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> RestResult() {
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">constructor</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> RestResult(Integer status, String msg, T data) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.code =<span style="color: rgba(0, 0, 0, 1)"> status;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.msg =<span style="color: rgba(0, 0, 0, 1)"> msg;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.data =<span style="color: rgba(0, 0, 0, 1)"> data;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回成功数据</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> RestResult success(T data) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RestResult(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getMsg(), data);
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> RestResult success(Integer code,String msg) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> RestResult(code, msg, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回出错数据</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> RestResult error(ResponseCode code) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> RestResult(code.getCode(), code.getMsg(), <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Integer getCode() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> code;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setCode(Integer code) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.code =<span style="color: rgba(0, 0, 0, 1)"> code;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getMsg() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> msg;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setMsg(String msg) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.msg =<span style="color: rgba(0, 0, 0, 1)"> msg;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> T getData() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> data;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setData(T data) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.data =<span style="color: rgba(0, 0, 0, 1)"> data;
}</span></pre>

说明:这里要注意使用泛型 T, 如果只用 Object, 则 swagger 不能识别我们所返回的数据的类型说明:

         其中:ApiModel 用于类上面说明功能,

               ApiModelProperty 用于字段上说明功能

        尤其是 getData 方法的返回数据类型,要用 T,使用工具生成的 data 类容易出现这种错误,

       

3,Goods.java

@ApiModel("商品模型")
public class Goods {
    //商品 id
    @ApiModelProperty("商品 id")
    Long goodsId;
    public Long getGoodsId() {
        return this.goodsId;
    }
    public void setGoodsId(Long goodsId) {
        this.goodsId = goodsId;
    }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">商品名称</span>
@ApiModelProperty("商品名称"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String goodsName;
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getGoodsName() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.goodsName;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setGoodsName(String goodsName) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.goodsName =<span style="color: rgba(0, 0, 0, 1)"> goodsName;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">商品标题</span>
@ApiModelProperty("商品标题"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String subject;
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getSubject() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.subject;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setSubject(String subject) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.subject =<span style="color: rgba(0, 0, 0, 1)"> subject;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">商品价格</span>
@ApiModelProperty("商品价格"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> BigDecimal price;
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> BigDecimal getPrice() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.price;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setPrice(BigDecimal price) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.price =<span style="color: rgba(0, 0, 0, 1)"> price;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">库存</span>
@ApiModelProperty("商品库存"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> stock;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getStock() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.stock;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setStock(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> stock) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.stock =<span style="color: rgba(0, 0, 0, 1)"> stock;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String toString(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> " Goods:goodsId=" + goodsId +" goodsName=" + goodsName+" subject=" + subject+" price=" + price+" stock=" +<span style="color: rgba(0, 0, 0, 1)"> stock;
}

}

 

4,GoodsController.java

@Api(tags = "商品信息管理接口")
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Operation(summary </span>= "商品详情,针对得到单个商品的信息"<span style="color: rgba(0, 0, 0, 1)">)
@GetMapping(</span>"/one"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> RestResult&lt;Goods&gt; one(@Parameter(description = "商品id,正整数") @RequestParam(value="goodsid",required = <span style="color: rgba(0, 0, 255, 1)">false</span>,defaultValue = "0"<span style="color: rgba(0, 0, 0, 1)">) Integer goodsid) {
    Goods goodsone </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Goods();
    goodsone.setGoodsId(</span>3L<span style="color: rgba(0, 0, 0, 1)">);
    goodsone.setGoodsName(</span>"电子书"<span style="color: rgba(0, 0, 0, 1)">);
    goodsone.setSubject(</span>"学python,学ai"<span style="color: rgba(0, 0, 0, 1)">);
    goodsone.setPrice(</span><span style="color: rgba(0, 0, 255, 1)">new</span> BigDecimal(60<span style="color: rgba(0, 0, 0, 1)">));
    goodsone.setStock(</span>10<span style="color: rgba(0, 0, 0, 1)">);
    RestResult res </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RestResult();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> res.success(goodsone);
}

@Operation(summary </span>= "提交订单"<span style="color: rgba(0, 0, 0, 1)">)
@PostMapping(</span>"/order"<span style="color: rgba(0, 0, 0, 1)">)
@ApiImplicitParams({
        @ApiImplicitParam(name</span>="userid",value="用户id",dataTypeClass = Long.<span style="color: rgba(0, 0, 255, 1)">class</span>, paramType = "query",example="12345"<span style="color: rgba(0, 0, 0, 1)">),
        @ApiImplicitParam(name</span>="goodsid",value="商品id",dataTypeClass = Integer.<span style="color: rgba(0, 0, 255, 1)">class</span>, paramType = "query",example="12345"<span style="color: rgba(0, 0, 0, 1)">),
        @ApiImplicitParam(name</span>="mobile",value="手机号",dataTypeClass = String.<span style="color: rgba(0, 0, 255, 1)">class</span>, paramType = "query",example="13866668888"<span style="color: rgba(0, 0, 0, 1)">),
        @ApiImplicitParam(name</span>="comment",value="发货备注",dataTypeClass = String.<span style="color: rgba(0, 0, 255, 1)">class</span>, paramType = "query",example="请在情人节当天送到"<span style="color: rgba(0, 0, 0, 1)">)
})

</span><span style="color: rgba(0, 0, 255, 1)">public</span> RestResult&lt;String&gt; order(@ApiIgnore @RequestParam Map&lt;String,String&gt;<span style="color: rgba(0, 0, 0, 1)"> params) {
    System.out.println(params);
    RestResult res </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RestResult();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> res.success("success"<span style="color: rgba(0, 0, 0, 1)">);
}

}

说明:Api 用来指定一个 controller 中的各个接口的通用说明

        Operation: 用来说明一个方法

        @ApiImplicitParams: 用来包含多个包含多个 @ApiImplicitParam,

        @ApiImplicitParam: 用来说明一个请求参数 

       如果使用 @Parameter 来做说明,可以直接加到 @RequestParam 参数之前         

       @ApiIgnore: 用来忽略不必要显示的参数

 

五,效果测试

1, 访问文档:访问:

http://127.0.0.1:8080/swagger-ui/index.html

可以看到已有的接口:

 

 

 

2, 查看接口的详情:

参数: 可以看到我们添加的全局通用参数

 

响应:

 

 

 

六,查看 spring boot 的版本

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
(()\___ | '_ |'_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ))) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.2.RELEASE)