一、SpringBoot 整合Swagger2 1.创建子模块 这里我们创建一个子模块
1 2 group = 'com.ray.study' artifact ='spring-boot-04-swagger2'
2.引入依赖 2.1 继承父工程依赖 在父工程spring-boot-seeds
的 settings.gradle
加入子工程
1 2 3 4 5 rootProject.name = 'spring-boot-seeds' include 'spring-boot-01-helloworld' include 'spring-boot-02-restful-test' include 'spring-boot-03-thymeleaf' include 'spring-boot-04-swagger2'
这样,子工程spring-boot-04-swagger2
就会自动继承父工程中subprojects
`函数里声明的依赖,主要包含如下依赖:
1 2 3 4 5 implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok'
2.2 引入swagger2
依赖 将子模块spring-boot-04-swagger2
的build.gradle
修改为如下内容:
1 2 3 4 dependencies { implementation 'io.springfox:springfox-swagger2:2.9.2' implementation 'io.springfox:springfox-swagger-ui:2.9.2' }
3.修改配置 3.1 修改application.yml
改下应用端口和上下文名称
1 2 3 4 5 server: port: 8088 servlet: context-path: /
3.2 新建Swagger2
配置类 在config
包下,创建配置类SwaggerConfig
,主要作用是注册Swagger
相关bean
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 package com.ray.study.springboot04swagger2.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.service.ApiInfo;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;@Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket createRestApi () { String basePackage = "com.ray.study.springboot04swagger2.controller" ; return new Docket (DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(basePackage)) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo () { return new ApiInfoBuilder () .title("SpringBoot_04_整合Swagger2构建RESTfulAPI文档" ) .description("原文地址:https://github.com/shirayner/java-knowledge-hierarchy" ) .termsOfServiceUrl("https://blog.csdn.net/qq_26981333/" ) .contact("shirayner" ) .version("v1.0" ) .build(); } }
Swagger
通过扫描指定包下符合指定路径匹配规则的 RequestMapping
来生成API接口文档
.apis(RequestHandlerSelectors.basePackage(basePackage))
:设置要扫描的包
.paths(PathSelectors.any())
:设置路径匹配规则
路径匹配规则可以采用正则表达式或Ant模式来设置,具体可看PathSelectors
源码
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 public class PathSelectors { private PathSelectors () { throw new UnsupportedOperationException (); } public static Predicate<String> any () { return Predicates.alwaysTrue(); } public static Predicate<String> none () { return Predicates.alwaysFalse(); } public static Predicate<String> regex (final String pathRegex) { return new Predicate <String>() { @Override public boolean apply (String input) { return input.matches(pathRegex); } }; } public static Predicate<String> ant (final String antPattern) { return new Predicate <String>() { @Override public boolean apply (String input) { AntPathMatcher matcher = new AntPathMatcher (); return matcher.match(antPattern, input); } }; } }
4.业务实现 通过完成前面的步骤
(1)引入swagger2
依赖
(2)新建swagger2
配置类
我们现在就可以使用swagger2
的注解来帮助我们生成API文档了,先看示例(主要拷贝[SpringBoot_02_构建RESTful API与单元测试](./SpringBoot_02_构建RESTful API与单元测试.md)中的代码),后面我们再对这些注解加以解释说明
4.1 model 这里用到了Swagger注解:
@ApiModel
:修饰实体类,描述实体类
@ApiModelProperty
:修饰实体类属性,描述属性
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 package com.ray.study.springboot04swagger2.model;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@ApiModel(description = "用户信息实体") @Data @NoArgsConstructor @AllArgsConstructor public class User { @ApiModelProperty("用户ID") private Long id; @ApiModelProperty("姓名") private String name; @ApiModelProperty("年龄") private Integer age; }
4.2 service 这里没有用到swagger
注解
UserService
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 package com.ray.study.springboot04swagger2.service;import com.ray.study.springboot04swagger2.model.User;import java.util.List;public interface UserService { List<User> list () ; User get (Long id) ; User insert (User user) ; User update (User user) ; boolean delete (Long id) ; }
UserServiceImpl
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 package com.ray.study.springboot04swagger2.service.impl;import com.ray.study.springboot04swagger2.model.User;import com.ray.study.springboot04swagger2.service.UserService;import org.springframework.stereotype.Service;import java.util.*;@Service public class UserServiceImpl implements UserService { private static Map<Long, User> users = Collections.synchronizedMap(new HashMap <>()); @Override public List<User> list () { return new ArrayList <>(users.values()); } @Override public User get (Long id) { return users.get(id); } @Override public User insert (User user) { users.put(user.getId(), user); return user; } @Override public User update (User user) { User u = users.get(user.getId()); u.setName(user.getName()); u.setAge(user.getAge()); users.put(user.getId(), u); return u; } @Override public boolean delete (Long id) { users.remove(id); return true ; } }
4.3 controller 这里用到了Swagger注解:
@Api
: 修饰Controller
类,对API接口进行描述
@ApiOperation
: 修饰Controller
方法,对具体的API接口方法进行描述
@ApiImplicitParam
:修饰Controller
方法,对具体的API接口方法的方法参数进行描述
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 package com.ray.study.springboot04swagger2.controller;import com.ray.study.springboot04swagger2.model.User;import com.ray.study.springboot04swagger2.service.UserService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiOperation;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@Api(tags = {"用户管理接口"}) @RestController @RequestMapping(value = "/users") public class UserController { @Autowired UserService userService; @ApiOperation(value = "查询文章用户列表") @GetMapping("/") public List<User> list () { return userService.list(); } @ApiOperation(value = "查询用户", notes = "根据用户id查询用户,用户id需大于0") @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long") @GetMapping("/{id}") public User get (@PathVariable Long id) { return userService.get(id); } @ApiOperation(value = "新增用户", notes="新增用户接口,用户id不用传,会自增") @ApiImplicitParam(name = "user", value = "用户信息实体", required = true, dataType = "User", paramType = "body") @PostMapping("/") public User insert (@RequestBody User user) { return userService.insert(user); } @ApiOperation(value = "更新用户") @ApiImplicitParam(name = "user", value = "用户信息实体", required = true, dataType = "User", paramType = "body") @PutMapping("/") public User update (@RequestBody User user) { return userService.update(user); } @ApiOperation(value = "删除用户") @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long") @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public String delete (@PathVariable Long id) { boolean result = userService.delete(id); return "success" ; } }
5.最终效果 访问如下网址,会出现下图:
http://localhost:8088/swagger-ui.html
6.相关异常 6.1.1 异常信息 访问Swagger首页时,出现以下异常信息:
1 2 3 4 5 6 7 8 9 10 11 java.lang .NumberFormatException : For input string : "" at java.lang .NumberFormatException .forInputString (NumberFormatException.java :65 ) ~[na:1 .8 .0_161 ] at java.lang .Long .parseLong (Long.java :601 ) ~[na:1 .8 .0_161 ] at java.lang .Long .valueOf (Long.java :803 ) ~[na:1 .8 .0_161 ] at io.swagger .models .parameters .AbstractSerializableParameter .getExample (AbstractSerializableParameter.java :412 ) ~[swagger-models-1 .5 .20 .jar :1 .5 .20 ] at sun.reflect .NativeMethodAccessorImpl .invoke0 (Native Method) ~[na:1 .8 .0_161 ] at sun.reflect .NativeMethodAccessorImpl .invoke (NativeMethodAccessorImpl.java :62 ) ~[na:1 .8 .0_161 ] at sun.reflect .DelegatingMethodAccessorImpl .invoke (DelegatingMethodAccessorImpl.java :43 ) ~[na:1 .8 .0_161 ] at java.lang .reflect .Method .invoke (Method.java :498 ) ~[na:1 .8 .0_161 ] at com.fasterxml .jackson .databind .ser .BeanPropertyWriter .serializeAsField (BeanPropertyWriter.java :688 ) [jackson-databind-2 .9 .8 .jar :2 .9 .8 ] at com.fasterxml .jackson .databind .ser .std .BeanSerializerBase .serializeFields (BeanSerializerBase.java :719 ) [jackson-databind-2 .9 .8 .jar :2 .9 .8 ]
参考资料:
6.1.2 异常原因 在 Swagger2 中, @ApiModelProperties
如果为数字类型,但添加注解后,又不指定example的值,会默认为””,swagger在后续处理的时候强行转化空字符串就会抛出异常
我们注意到异常信息中的这一行:
1 at io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412)
对应源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @JsonProperty("x-example") public Object getExample () { if (example == null ) { return null ; } try { if (BaseIntegerProperty.TYPE.equals(type)) { return Long.valueOf(example); } else if (DecimalProperty.TYPE.equals(type)) { return Double.valueOf(example); } else if (BooleanProperty.TYPE.equals(type)) { if ("true" .equalsIgnoreCase(example) || "false" .equalsIgnoreCase(defaultValue)) { return Boolean.valueOf(example); } } } catch (NumberFormatException e) { LOGGER.warn(String.format("Illegal DefaultValue %s for parameter type %s" , defaultValue, type), e); } return example; }
由于默认 example 为空串””,因此在转Long的时候,报数字转型异常。
6.1.3 异常解决 给字段类型为数值类型(Integer/Long等)的 @ApiModelProperties
增加example
属性的赋值即可,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ApiModel(description = "用户信息实体") @Data @NoArgsConstructor @AllArgsConstructor public class User { @ApiModelProperty(value = "用户ID", example = "10001") private Long id; @ApiModelProperty("姓名") private String name; @ApiModelProperty(value = "年龄", example = "21") private Integer age; }
二、Swagger注解说明 Swagger提供了如下注解
这里我们只对常用注解进行说明
另外若想了解注解的所有属性,可去看源码注释,也可参考:swagger常用注解说明
下面通过一张图来对每个注解负责那一块区域做一个大概的了解:
1. @Api
修饰Controller
类,对此API接口合辑进行描述
属性
含义
说明
value
值
若设置了tag
,则value
会被忽略;若没有设置tag
,则将value
赋值给tag
tags
标签
用于对swagger
资源操作的逻辑分组
hidden
是否隐藏
默认为false,若为ture,则将在API文档中隐藏
示例:
1 2 3 4 @Api(tags = {"用户管理接口"}) @RestController @RequestMapping(value = "/users") public class UserController {
2. @ApiOperation
修饰Controller
方法(准确地说是RequestMapping方法),对API接口方法进行描述
属性
含义
说明
value
值
对操作进行一个简短的描述,最长不超过120个字符
notes
注释
用于对swagger
资源操作的逻辑分组
hidden
是否隐藏
默认为false,若为ture,则将在API文档中隐藏
示例:
1 2 3 4 5 6 @ApiOperation(value = "查询用户", notes = "根据用户id查询用户,用户id需大于0") @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long") @GetMapping("/{id}") public User get (@PathVariable Long id) { return userService.get(id); }
3.@ApiImplicitParams
/@ApiImplicitParam
修饰Controller
方法,对API接口方法的方法参数进行描述
ApiImplicitParam
的属性如下:
属性
含义
说明
name
参数名称
通常与方法的参数名称保持一致
value
值
对方法参数的简短描述
defaultValue
默认值
描述方法参数的默认值
required
是否必需
默认为false,非必需
dataType
数据类型
方法参数的数据类型,默认为String,可设置为其他基本类型或类名
paramType
参数来源类型
有效值为:path、query、body、header、form
readOnly
是否只读
默认为false,若为true,则在API文档中无法编辑此属性的值
示例:
1 2 3 4 5 6 @ApiOperation(value = "新增用户") @ApiImplicitParam(name = "user", value = "用户信息实体", required = true, dataType = "User", paramType = "body") @PostMapping("/") public User insert (@RequestBody User user) { return userService.insert(user); }
4.@ApiResponses
/@ApiResponse
修饰Controller
类或者Controller
方法,对响应结果进行描述,一般用于表达一个错误的响应信息
属性名称
说明
code
http的状态码
message
状态的描述信息
response
状态相应,默认响应类 Void
示例 :
1 2 3 4 5 @ApiResponses({ @ApiResponse(code = 400, message = "Invalid ID supplied"), @ApiResponse(code = 404, message = "Pet not found") }) public Response getPetById (...) {...}
5.@ApiIgnore
修饰Controller
类、方法或者方法参数,用于在文档中忽略这个API
属性名称
说明
value
默认为空串,简要描述为什么要忽略此API
示例:
1 2 @ApiIgnore public boolean delete (@PathVariable("id") int id)
6.@ApiModel
修饰 model
,用于描述Swagger模型
属性名称
说明
value
默认为model
类名,用于指定Swagger模型的名称
description
对model
进行描述
parent
指定父类,可以继承父类的描述
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ApiModel(description = "用户信息实体") @Data @NoArgsConstructor @AllArgsConstructor public class User { @ApiModelProperty(value = "用户ID", example = "10001") private Long id; @ApiModelProperty("姓名") private String name; @ApiModelProperty(value = "年龄", example = "21") private Integer age; }
7.@ApiModelProperty
修饰model
属性,用于描述Swagger模型的属性
属性名称
说明
value
对参数的简要描述
name
参数名,用于覆盖默认的参数名(即model属性名),不常用
dataType
参数的数据类型,用于覆盖默认的参数类型(即model属性数据类型),不常用
example
参数值的示例
required
是否必需
hidden
是否隐藏
注意:
在 Swagger2 中, @ApiModelProperties
如果为数值类型,但添加注解后,又不指定example的值,会默认为””,swagger在后续处理的时候强行转化空字符串就会抛出异常,见 第一部分第6小节[相关异常]
因此,对数据类型为数值类型的属性,添加@ApiModelProperties
注解时,要对 example
属性进行赋值
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ApiModel(description = "用户信息实体") @Data @NoArgsConstructor @AllArgsConstructor public class User { @ApiModelProperty(value = "用户ID", example = "10001") private Long id; @ApiModelProperty("姓名") private String name; @ApiModelProperty(value = "年龄", example = "21") private Integer age; }