写在前面

本文将在第一篇《项目骨架搭建》的基础上整合Swagger-UI,实现可以在线阅读API文档这一功能。

Swagger-UI简介

Swagger-UI是HTML、CSS和Javascript的一个集合,可以动态地根据注解生成在线API文档,本篇将要整合的Swagger-UI版本为2系列。

Swagger-UI常用的一些注解如下所示:
(1)@Api:用于修饰Controller类,可生成Controller相关的文档信息;
(2)@ApiOperation:用于修饰Controller类中的方法,可生成接口方法相关的文档信息;
(3)@ApiParam:用于修饰接口中方法的参数,可生成接口参数相关的文档信息;
(4)@ApiModelProperty:用于修饰实体类的属性,当实体类是请求参数或返回结果时,会直接生成相关的文档信息。

关于Swagger-UI的详细介绍,可以参阅笔者其他的文章,此处不做过多介绍。

整合Swagger-UI

第一步,复制一份shop-basic源码,将其名字修改为shop-swagger-ui,然后对应包和文件中的信息也记得修改,本篇后续所有操作均在shop-swagger-ui这一Module中进行。

第二步,在shop-swagger-ui的POM文件中新增如下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 <!-- MyBatis 生成器 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
<!--Swagger-UI API文档-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>

注意,mybatis-generator-core的版本必须从1.3.3修改为1.3.7,否则后续在生成API文档的时候会出现异常问题,以及新生成的mapper文件无法覆盖旧文件的情况。

第三步,在config包内新建一个名为Swagger2Config的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* Swagger2 API文档相关配置
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//为当前包下controller生成API文档
.apis(RequestHandlerSelectors.basePackage("com.kenbings.shop.shopswaggerui.controller"))
//为有@Api注解的Controller生成API文档
// .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
//为有@ApiOperation注解的方法生成API文档
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("整合SwaggerUI")
.description("myshop-all")
.contact("kenbings")
.version("1.0")
.build();
}
}

请注意,Swagger UI对生成API文档的范围有三种选择,分别如下所示:
(1)生成指定包下类的API文档:

1
.apis(RequestHandlerSelectors.basePackage("com.kenbings.shop.shopswaggerui.controller"))

(2)生成有指定注解的类的API文档:

1
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))

(3)生成有指定注解的方法的API文档:

1
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))

第四步,给需要生成的Controller类添加Swagger注解,此处选择之前使用的品牌管理PmsBrandController类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* 品牌管理Controller
*/
@Api(tags = "PmsBrandController",description = "商品品牌管理")
@RestController
@RequestMapping("/brand")
public class PmsBrandController {
@Autowired
private PmsBrandService pmsBrandService;

private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);

/**
* 查询所有的品牌信息
*/
@ApiOperation("获取所有的品牌信息")
@GetMapping("/listAll")
public CommonResult<List<PmsBrand>> getBrandList(){
return CommonResult.success(pmsBrandService.listAllBrand());
}

/**
* 创建一个新品牌
*/
@ApiOperation("添加品牌")
@PostMapping("/create")
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand){
CommonResult commonResult;
int count = pmsBrandService.createBrand(pmsBrand);
if(count==1){
commonResult = CommonResult.success(pmsBrand);
LOGGER.debug("品牌创建成功:{}",pmsBrand);
}else{
commonResult = CommonResult.failed("品牌创建失败");
LOGGER.debug("品牌创建失败:{}",pmsBrand);
}
return commonResult;
}

/**
* 修改一个品牌信息
*/
@ApiOperation("更新指定id的品牌信息")
@PostMapping("/update/{id}")
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDTO, BindingResult result){
CommonResult commonResult;
if(result.hasErrors()){
commonResult = CommonResult.failed(result.getFieldError().getDefaultMessage());
return commonResult;
}
int count = pmsBrandService.updateBrand(id,pmsBrandDTO);
if(count==1){
commonResult = CommonResult.success(pmsBrandDTO);
LOGGER.debug("品牌修改成功:{}",pmsBrandDTO);
}else{
commonResult = CommonResult.failed("品牌修改失败");
LOGGER.debug("品牌修改失败:{}",pmsBrandDTO);
}
return commonResult;
}

/**
* 删除一个品牌
*/
@ApiOperation("删除指定id的品牌")
@PostMapping("/delete/{id}")
public CommonResult deleteBrand(@PathVariable("id") Long id){
CommonResult commonResult;
int count = pmsBrandService.deleteBrand(id);
if(count==1){
commonResult = CommonResult.success(null);
LOGGER.debug("品牌删除成功:{}",id);
}else{
commonResult = CommonResult.failed("品牌修改失败");
LOGGER.debug("品牌删除失败:{}",id);
}
return commonResult;
}

/**
* 分页查询品牌信息
*/
@ApiOperation("分页查询品牌列表")
@GetMapping("/list")
public CommonResult<CommonPage<PmsBrand>> listBrand(
@RequestParam(value = "pageNum",defaultValue = "1")
@ApiParam("页码")Integer pageNum,
@RequestParam(value = "pageSize",defaultValue = "5")
@ApiParam("每页数量") Integer pageSize
){
List<PmsBrand> pmsBrands = pmsBrandService.listBrand(pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(pmsBrands));
}

/**
* 获取某个品牌信息
*/
@ApiOperation("获取指定id的品牌详情")
@GetMapping("/{id}")
public CommonResult<PmsBrand> getBrand(@PathVariable("id")Long id){
return CommonResult.success(pmsBrandService.getBrand(id));
}
}

这里我们充分使用了@Api@ApiOperation@ApiParam注解,没有使用到@ApiModelProperty注解,那是因为@ApiModelProperty注解用于修饰实体类的属性,我们如果要使用它,就要修改MyBatis Generator注释的生成规则。

第五步,修改MyBatis Generator注释的生成规则。在前面我们说过,CommentGenerator类是自定义注释生成器,我们可以修改addFieldComment方法,使其可以生成Swagger的@ApiModelProperty注解来取代原来的方法注释。同时还需要添加addJavaFileComment方法,使其能在import中导入@ApiModelProperty,否则需要手动导入该类,这在需要生成大量实体类时,是一件非常痛苦的事情。

CommentGenerator类中的代码修改为如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 自定义注释生成器
*/
public class CommentGenerator extends DefaultCommentGenerator {
private boolean addRemarkComments = false;
private static final String EXAMPLE_SUFFIX="Example";
private static final String API_MODEL_PROPERTY_FULL_CLASS_NAME="io.swagger.annotations.ApiModelProperty";

/**
*设置用户配置的参数
*/
@Override
public void addConfigurationProperties(Properties properties) {
super.addConfigurationProperties(properties);
this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments"));
}

/**
* 给字段添加注释
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
String remarks = introspectedColumn.getRemarks();
//根据参数和备注信息来判断是否添加备注信息
if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
//数据库中特殊字符需要转义
if(remarks.contains("\"")){
remarks = remarks.replace("\"","'");
}
//给model的字段添加swagger注解
field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")");
}
}

@Override
public void addJavaFileComment(CompilationUnit compilationUnit) {
super.addJavaFileComment(compilationUnit);
//只在model中添加swagger注解类的导入
if(!compilationUnit.isJavaInterface()&&!compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)){
compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME));
}
}
}

第六步,在generatorConfig.xml文件中新增一行配置信息:

1
2
<!--生成mapper.xml时覆盖原文件-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />

注意配置项的放置位置:

第七步,执行Generator类中的main方法,可以看到此时会重新生成mbg中的代码。

可以看到新生成的PmsBrand类里面,已经根据数据库注释自动添加了@ApiModelProperty注解:

第八步,启动项目,访问Swagger-UI接口文档地址,即浏览器访问http://localhost:8080/swagger-ui.html链接,效果如下:

点击添加品牌按钮,可以看到里面已经显示从@ApiModelProperty文件中生成的说明信息:

点击分页查询品牌列表,可以看到返回结果也已经进行了说明:

同时开发者可以直接在文档上进行接口测试,这里以查询id为1的品牌详情信息为例进行说明:

这样本篇关于整合Swagger-UI实现在线API文档的学习就完成了,后续介绍如何整合单机版Redis实现缓存这一功能。本篇笔记源码,可以点击 这里 进行阅读。