一、简介

Spring Boot完整教程 | 主页及目录

swagger

在前后端分离的项目中,接口文档是前后端沟通联调的重要工具。一般我们后端人员会使用接口文档工具(如小幺鸡甚至Excel等)编写接口文档。对于后端开发来说,编写这个json格式的接口说明,本身也是有一定负担的工作,特别是在后面持续迭代开发的时候,往往会忽略更新这个接口说明,直接更改代码。久而久之,接口文档由于缺乏维护,也和实际项目渐行渐远,甚至失去了参考意义。(Ps:部门目前需要对外发布的接口文档以及需要特殊描述的接口仍使用工具进行人工编写)

因此我们希望引入某项工具,可以扫描相关的代码,动态生成与代码一致的接口说明。基于此背景,出现了Swagger 框架。Swagger定义了一套接口规范,通过这套规范,你只需要按照它的规范去定义接口及接口相关的信息。再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,生成多种语言的客户端和服务端的代码,以及在线接口调试页面等等。

springfox-swagger

springfox-swagger是一套基于Swagger(结合Spring与Swagger),帮助开发者自动生成API文档的工具。由于UI页面不是很清晰友好,因此国人基于springfox-swagger打造出了swagger-bootstrap-ui。

swagger-bootstrap-ui

swagger-bootstrap-ui是基于springfox-swagger的增强UI实现,以文档说明和在线调试为核心,为Java开发者在使用Swagger的时候,能拥有一份简洁、强大的接口文档体验。Swagger-Bootstrap-UI 替换Swagger 默认的UI实现左右菜单风格的Swagger-UI ,让其看起来更清晰明了。

knife4j

随着项目略显臃肿,作者将项目正式更名为knife4j,并做了调整改进。

(一句话总结就是:knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案)

作者原文如下:“但是随着项目的发展,面对越来越多的个性化需求,不得不编写后端Java代码以满足新的需求,在swagger-bootstrap-ui的1.8.5~1.9.6版本之间,采用的是后端Java代码和Ui都混合在一个Jar包里面的方式提供给开发者使用.这种方式虽说对于集成swagger来说很方便,只需要引入jar包即可,但是在微服务架构下显得有些臃肿。因此,项目正式更名为knife4j,取名knife4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍,更名也是希望把她做成一个为Swagger接口文档服务的通用性解决方案,不仅仅只是专注于前端Ui前端”

官网地址:https://doc.xiaominfo.com/knife4j/

二、搭建

2.1使用2.0.2版本

引入pom依赖

        <!-- knife4j(swagger)依赖 -->         <dependency>             <groupId>com.github.xiaoymin</groupId>             <artifactId>knife4j-spring-boot-starter</artifactId>             <!--在引用时请在maven中央仓库搜索最新版本号-->             <version>2.0.2</version>         </dependency>

新建config包,创建swagger的配置类

配置类代码如下(注意按照自己的项目配置包扫描路径):

package com.kcsm.training.bootdemo.config;  import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;  @Configuration @EnableSwagger2 @EnableKnife4j @Import(BeanValidatorPluginsConfiguration.class) public class SwaggerConfiguration {       @Bean     public Docket defaultApi2() {         Docket docket=new Docket(DocumentationType.SWAGGER_2)                 .apiInfo(apiInfo())                 //分组名称                 .groupName("1.0版本")                 .select()                 //这里指定Controller扫描包路径(项目路径也行)                 .apis(RequestHandlerSelectors.basePackage("com.kcsm.training.bootdemo.controller"))                 .paths(PathSelectors.any())                 .build();         return docket;     }     private ApiInfo apiInfo() {         return new ApiInfoBuilder()                 .title("接口说明")                 .description("DEMO服务接口说明")                 .termsOfServiceUrl("http://localhost:88888/")                 .version("1.0")                 .build();     } }

 启动项目成功后,访问http://localhost:8080/doc.html,可访问即说明引入及配置成功

上述配置已允许开启了增强功能,开启增强功能(显示作者信息,排序等)需要在打开的页面上进行勾选方才生效

2.2 使用2.0.7以上版本

特别说明:使用Knife4j2.0.6及以上的版本,Spring Boot的版本必须大于等于2.2.x

请直接按照官网步骤搭建完成后,学习下一节的基本使用和实际项目中的使用方法,官网地址:https://doc.xiaominfo.com/knife4j/documentation/get_start.html

三、基本使用

3.1 项目常用注解使用

@Api():用于类的说明

@ApiOperation():用于方法的说明

@ApiOperationSupport():(knife4j增加特性)用于接口方法排序,作者信息描述等。

@ApiImplicitParam():对单个参数的说明

@ApiModel():用于描述一个数据模型的信息,即我们常用的实体、VO类、DTO类等描述

@ApiModelProperty():用于描述数据模型的属性信息

@ApiIgnore:自动生成接口说明时忽略

下面以“根据性别查找所有学生”接口为例进行说明,我们需要对类进行描述、对方法进行描述、对请求参数进行描述、对返回的数据进行描述,完整代码如下:

controller层

package com.kcsm.training.bootdemo.controller;  import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiSort; import com.kcsm.training.bootdemo.common.JsonResult; import com.kcsm.training.bootdemo.controller.vo.GetParaVo; import com.kcsm.training.bootdemo.entity.Student; import com.kcsm.training.bootdemo.service.StudentService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.data.repository.query.Param; import org.springframework.web.bind.annotation.*; import springfox.documentation.annotations.ApiIgnore;  import javax.annotation.Resource; import java.util.List;  /**  * 学生信息控制类  */  @Api(tags = "学生信息接口") @RestController @RequestMapping(value = "student") @ApiSort(1) public class StudentController {     @Resource(name="studentServiceImpl")     StudentService studentService;      /**      * 根据性别查询学生信息      *      * @author lqk      * @param  gender [String]性别      * @return java.util.List<com.kcsm.training.bootdemo.entity.Student>      * @date   2019/7/10 9:16      */     @RequestMapping(value = "v1/jpatest",method = RequestMethod.GET)     @ApiOperation(value = "根据性别查找所有学生")     @ApiOperationSupport(order=1,author ="陆启坤")     @ApiImplicitParam(name = "gender", value = "性别",required = true,dataType = "String", defaultValue = "male")     public List<Student> findAllByGender(String gender){         return studentService.findAllByGender(gender);     }  }

返回的数据模型(即为学生实体类)

package com.kcsm.training.bootdemo.entity;  import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.hibernate.annotations.GenericGenerator;  import javax.persistence.*;  /**  * 学生实体类  */ @Entity @Table(name = "STUDENT") @Data @ApiModel("学生实体类") public class Student {      /**      * 主键      */     @Id     @GeneratedValue(generator = "guidGenerator")     @GenericGenerator(name = "guidGenerator", strategy = "uuid")     @Column(name = "ID", unique = true, nullable = false, length = 32)     @ApiModelProperty(value = "主键")     private String id;      @Column(name="NAME",length=50)     @ApiModelProperty(value = "姓名",example="张三")     private String name;      @Column(name="AGE")     @ApiModelProperty(value = "年龄")     private Integer age;      /**      * 性别,male:男性,female:女性      */     @Column(name="GENDER",length=10)     @ApiModelProperty(value = "性别(male:男性,female:女性)",example = "male")     private String gender;  }

显示效果如下

同时可直接进行接口调试

注意当使用@RequestMapping,而不指定请求类型时,将自动生成多个接口说明(get、post、delete等),对于不想生成说明的类或方法,使用@ApiIgnore注解即可屏蔽

    @ApiIgnore     @RequestMapping(value = "v1/findByGenderAndName")     public List<Student> findByGenderAndName(String gender,String name){         return studentService.findByGenderAndName(gender,name);     }

四、项目中常用使用方法

在我们实际项目中,在请求参数较多的情况下我们通常会将请求参数封装成VO类,在返回前端时,我们一般会定义一个返回类的数据模型JsonResult或者ReturnData等,此种情况只需在数据模型使用@ApiModel()、@ApiModelProperty()等对类及属性进行说明,即可自动生成接口说明。下面使用"根据性别以及姓名查找所有学生"接口进行说明

封装请求参数模型GetParaVo

package com.kcsm.training.bootdemo.controller.vo;  import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data;  @Data @ApiModel("获取学生信息参数VO类") public class GetParaVo {     @ApiModelProperty(value = "性别(male:男性,female:女性)",required = true)     String gender;     @ApiModelProperty(value = "姓名",required = true)     String name; }

封装返回数据模型JsonResult(根据项目习惯设置,保证传入数据模型类型即可)

package com.kcsm.training.bootdemo.common;  import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty;  import java.util.List;  /**  * @Description: 接口请求返回数据模型  *   * @author: lqk  * @date: 2018年5月15日 上午11:55:24  */ @ApiModel("返回信息数据模型") public class JsonResult<T> { 	/** 	 * 状态码 	 */ 	@ApiModelProperty(value = "状态码(200:成功、500:失败、10000段参数错误、20000段业务错误)") 	private int state;  	/** 	 * 执行信息 	 */ 	@ApiModelProperty(value = "执行信息") 	private String msg;  	/** 	 * 传输的数据 	 */ 	@ApiModelProperty(value = "返回数据") 	private T data;  	// ------------------- 成功、失败返回码-----------------------/ 	/** 	 * 执行成功状态 	 */ 	private static final int SUCCESS = 200;  	/** 	 * 执行失败状态 	 */ 	private static final int ERROR = 500;  	// ------------------- 参数错误返回码:10001-19999-----------------/ 	/** 	 * 参数为空 	 */ 	public static final int PARAM_IS_BLANK = 10001;  	/** 	 * 参数类型错误 	 */ 	public static final int PARAM_TYPE_ERROR = 10002;  	/** 	 * 参数缺失 	 */ 	public static final int PARAM_NOT_COMPLETE = 10003;  	// -------------------登录注册错误:20001-29999-----------------/ 	/** 	 * 用户未登录 	 */ 	public static final int USER_NOT_LOGGED_IN = 20001;  	/** 	 * 用户不存在 	 */ 	public static final int USER_NOT_EXIST = 20002;  	/** 	 * 用户已存在 	 */ 	public static final int USER_HAS_EXISTED = 20003;  	/** 	 * 账号密码错误 	 */ 	public static final int USER_LOGIN_ERROR = 20004;  	/** 	 * 账号已被禁用 	 */ 	public static final int USER_ACCOUNT_FORBIDDEN = 20005;  	// -------------------业务错误:20001-29999-----------------/ 	/** 	 * 业务错误码 	 */ 	public static final int BUSINESS_ERROR_CODE = 20001; 	 	/** 	 * 业务成功,通知失败 	 */ 	public static final int NOTICE_ERROR_BUSINESS_SUCCESS = 3001; 	/** 	 * 成功消息 	 */ 	public static final String MESSAGE_SUCCESS = "执行成功!";  	public JsonResult() { 		this.state = SUCCESS; 		this.msg = "执行成功!"; 	}  	public JsonResult(int state, String msg, T data) { 		this.state = state; 		this.msg = msg; 		this.data = data; 	}  	public JsonResult(T data) { 		this.state = SUCCESS; 		this.msg = "获取数据成功!"; 		this.data = data; 	}  	public JsonResult(Exception ex) { 		this.state = ERROR; 		this.msg = ex.getMessage(); 	}  	public JsonResult(RuntimeException ex) { 		this.state = ERROR; 		this.msg = ex.getMessage(); 	}  	@Override 	public String toString() { 		return "JsonResult [state=" + state + ", msg=" + msg + ", data=" + data + "]"; 	}  }

controller方法

    @ApiOperation("根据性别以及姓名查找所有学生")     @ApiOperationSupport(order=2)     @GetMapping(value = "v1/findByGenderAndName" )     public JsonResult<Student> findByGenderAndName(GetParaVo getParaVo){         String gender=getParaVo.getGender();         String name = getParaVo.getName();         return  new JsonResult(studentService.findByGenderAndName(gender,name));     }

效果如下,通过对封装的参数模型、返回数据模型进行注解说明后,能自动生成相应的接口说明:

五、进阶使用

4.1 接口排序(knife4j新增的典型特性)

类说明,tags的分组排序

@Api(tags = "学生信息接口") @RestController @RequestMapping(value = "student") @ApiSort(1) public class StudentController{  }

tags下的接口排序

    /**      * 根据性别查询学生信息      *      * @author lqk      * @param  gender [String]性别      * @return java.util.List<com.kcsm.training.bootdemo.entity.Student>      * @date   2019/7/10 9:16      */     @RequestMapping(value = "v1/jpatest",method = RequestMethod.GET)     @ApiOperation("根据性别查找所有学生")     @ApiOperationSupport(order=1,author ="陆启坤")     @ApiImplicitParam(name = "gender", value = "性别",required = true,dataType = "String", defaultValue = "男")     public List<Student> findAllByGender(String gender){         return studentService.findAllByGender(gender);     }

4.2权限控制(knife4j新增的典型特性)

当我们部署系统到生产系统,为了接口安全,需要屏蔽所有Swagger的相关资源,只需在application.properties或者application.yml配置文件中配置

knife4j.production=true

即可屏蔽所有Swagger页面权限

另外也可以提供一个登陆界面的功能,开发者输入用户名和密码来控制界面的访问,只有知道用户名和密码的人才能访问此文档。如在application.properties或者application.yml配置文件中配置

## 开启Swagger的Basic认证功能,默认是false knife4j.basic.enable=true ## Basic认证用户名 knife4j.basic.username=admin ## Basic认证密码 knife4j.basic.password=123321

其他更多使用可参考:https://doc.xiaominfo.com/knife4j/documentation/changelog.html

五、常用注解说明

@Api

作用在类上,用来标注该类具体实现内容。参数:
tags:类标签,一般用来写类的名称或作用。(常用)
description:可描述描述该类作用。

@ApiOperation()

用于方法的说明,参数:

value :方法说明(常用)

notes :注释说明

httpMethod : 说明这个方法被请求的方式

response :方法的返回值的类型

@ApiOperationSupport()

(knife4j增加特性)用于接口方法排序,作者信息描述等。参数:

order:排序

author:作者信息

@ApiImplicitParam()

对单个参数的说明,参数:

1. name :参数名。
2. value : 参数的具体意义,作用。(常用)
3. required : 参数是否必填。 (常用)
4. dataType :参数的数据类型。 (常用)
5. paramType :查询参数类型,这里有几种形式:

类型            作用
path      以地址的形式提交数据
query    直接跟参数完成自动映射赋值
body      以流的形式提交 仅支持POST
header  参数在request headers 里边提交
form      以form表单的形式提交 仅支持POST

@ApiModel()

用于描述一个数据模型的信息,即我们常用的实体、VO类、DTO类等描述。参数:
value : 数据模型名称。(常用)

description:具体描述

parent:父类

@ApiModelProperty():

用于描述数据模型的属性信息,参数:

value:字段说明 (常用)
name:重写属性名字
dataType:重写属性类型
required:是否必填 (常用)
example:举例说明 (常用)
hidden:隐藏

@ApiIgnore

方法自动生成接口说明时忽略

@JsonIgnore

字段自动生成接口说明时忽略,即忽略某一个字段