在前后端分离的Web开发时代,无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新,所以就导致了相互撕逼的现象,其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是这个接口文档对于程序员来说,就跟注释一样,经常会抱怨别人写的代码没有写注释,然而自己写起代码起来,最讨厌的,也是写注释。所以仅仅只通过强制来规范大家是不够的,随着时间推移,版本迭代,接口文档往往很容易就跟不上代码,于是这就有了SpringBoot集成系列的第一篇Blog,关于SpringBoot如何集成Swagger来应用于前后端分离站点的开发。
Swagger基本概念
发现了前后端分离开发的痛点就要去找解决方案。解决方案用的人多了,就成了标准的规范,这就是Swagger的由来。通过这套规范,你只需要按照它的规范去定义接口及接口相关的信息。再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,生成多种语言的客户端和服务端的代码,以及在线接口调试页面等等。这样,如果按照新的开发模式,在开发新版本或者迭代版本的时候,只需要更新Swagger描述文件,就可以自动生成接口文档和客户端服务端代码,做到调用端代码、服务端代码以及接口文档的一致性:
- 官方站点为:https://swagger.io/
- 号称世界上最流行的API框架
- Restful Api 文档在线自动生成器 => API 文档 与API 定义同步更新
官方站点布局如下:
SpringBoot集成Swagger
对于许多开发来说,编写yml或json格式的描述文件,本身也是有一定负担的工作,特别是在后面持续迭代开发的时候,往往会忽略更新这个描述文件,直接更改代码。久而久之,这个描述文件也和实际项目渐行渐远,基于该描述文件生成的接口文档也失去了参考意义。所以作为Java届服务端的大一统框架Spring,迅速将Swagger规范纳入自身的标准,建立了Spring-Swagger
项目,后面改成了现在的Springfox
。通过在项目中引入Springfox
,可以扫描相关的代码,生成该描述文件,进而生成与代码一致的接口文档和客户端代码。这种通过代码生成接口文档的形式,在后面需求持续迭代的项目中,显得尤为重要和高效
1 添加Maven依赖
要使我们的项目集成Swagger,按照如下步骤进行配置即可:首先给项目中添加Maven依赖:springfox-boot-starter
以及springfox-swagger-ui
pom依赖坐标如下:
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-boot-starter --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>3.0.0</version> </dependency>
2 注解启动程序
添加完依赖后我们还需要注解启动程序,启用Swagger的配置:
SpringbootApplication
package com.example.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import springfox.documentation.oas.annotations.EnableOpenApi; @EnableAsync //开启异步注解功能 @EnableScheduling //开启基于注解的定时任务 @SpringBootApplication @EnableOpenApi public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
3 配置Profile多环境
我们可以添加 3 个配置文件,如下所示:
- application-dev.yml:开发环境配置
- application-test.yml:测试环境配置
- application-prod.yml:生产环境配置
在 applcation.yml 文件中指定默认服务端口号为 8080,并通过以下配置来激活开发环境的 profile
#数据源连接信息 spring: #环境配置 profiles: active: '@env@'
4 配置SwaggerConfig文件
注解好启动程序后我们需要将Swagger需要的配置通过配置类引入:
SwaggerConfig
package com.example.springboot.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.env.Profiles; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.oas.annotations.EnableOpenApi; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; @Configuration //配置类 @EnableOpenApi public class SwaggerConfig { @Bean public Docket createRestApi(Environment environment) { // 设置要显示swagger的环境 Profiles of = Profiles.of("dev", "test"); // 判断当前是否处于该环境,通过 enable() 接收此参数判断是否要显示 boolean isShow = environment.acceptsProfiles(of); return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()) .groupName("createRestApi") // 配置分组 //是否开启 (true 开启 false隐藏。生产环境建议隐藏) .enable(isShow) //通过.select()方法,去配置扫描接口 .select() //扫描的路径包,设置basePackage会将包下的所有被@Api标记类的所有方法作为api .apis(RequestHandlerSelectors.basePackage("com.example.springboot.controller")) //指定路径处理,PathSelectors.any()代表所有的路径 .paths(PathSelectors.any()) .build(); } @Bean public Docket createRestApi2(Environment environment) { // 设置要显示swagger的环境 Profiles of = Profiles.of("dev", "test"); // 判断当前是否处于该环境,通过 enable() 接收此参数判断是否要显示 boolean isShow = environment.acceptsProfiles(of); return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()) .groupName("tml") // 配置分组 //是否开启 (true 开启 false隐藏。生产环境建议隐藏) .enable(isShow) //通过.select()方法,去配置扫描接口 .select() //扫描的路径包,设置basePackage会将包下的所有被@Api标记类的所有方法作为api .apis(RequestHandlerSelectors.basePackage("com.example.springboot.controller")) //指定路径处理,PathSelectors.any()代表所有的路径 .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() //设置文档标题(API名称) .title("SpringBoot中使用Swagger3接口规范") //文档描述 .description("接口说明") //服务条款URL .termsOfServiceUrl("http://localhost:8080/") //版本号 .version("1.0.0") .build(); } }
以上配置中指定路径处理的方式apis
以及paths
还有如下几种方法可选:
any() // 扫描所有,项目中的所有接口都会被扫描到 none() // 不扫描接口 // 通过方法上的注解扫描,如withMethodAnnotation(GetMapping.class)只扫描get请求 withMethodAnnotation(final Class<? extends Annotation> annotation) // 通过类上的注解扫描,如.withClassAnnotation(Controller.class)只扫描有controller注解的类中的接口 withClassAnnotation(final Class<? extends Annotation> annotation) basePackage(final String basePackage) // 根据包路径扫描接口
5 接口服务相关配置
接下来我们编写正常的请求内容,注解的解释如下
Swagger注解 | 注解使用方式 |
@Api(tags = “xxx模块说明”) | 作用在Controller类上 |
@ApiOperation(“xxx接口说明”) | 作用在接口方法上 |
@ApiModel(“xxxPOJO说明”) | 作用在模型类上:如VO、BO |
@ApiModelProperty(value = “xxx属性说明”,hidden = true) | 作用在类方法和属性上,hidden设置为true可以隐藏该属性 |
@ApiParam(“xxx参数说明”) | 作用在参数、方法和字段上,类似@ApiModelProperty |
结构化说明如下:
@Api:用在请求的类上,表示对类的说明 tags="说明该类的作用,可以在UI界面上看到的注解" value="该参数没什么意义,在UI界面上也看到,所以不需要配置" @ApiOperation:用在请求的方法上,说明方法的用途、作用 value="说明方法的用途、作用" notes="方法的备注说明" @ApiImplicitParams:用在请求的方法上,表示一组参数说明 @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面 name:参数名 value:参数的汉字说明、解释 required:参数是否必须传 paramType:参数放在哪个地方 · header --> 请求参数的获取:@RequestHeader · query --> 请求参数的获取:@RequestParam · path(用于restful接口)--> 请求参数的获取:@PathVariable · body(不常用) · form(不常用) dataType:参数类型,默认String,其它值dataType="Integer" defaultValue:参数的默认值 @ApiResponses:用在请求的方法上,表示一组响应 @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息 code:数字,例如400 message:信息,例如"请求参数没填好" response:抛出异常的类 @ApiModel:用于响应类上,表示一个返回响应数据的信息 (这种一般用在post创建的时候,使用@RequestBody这样的场景, 请求参数无法使用@ApiImplicitParam注解进行描述的时候) @ApiModelProperty:用在属性上,描述响应类的属性
1 实体Model配置
首先我们需要编写一个接口入参的Model
Person
package com.example.springboot.model; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /* * person表对应对象 * */ @Data @AllArgsConstructor @NoArgsConstructor @ApiModel("人员实体") public class Person { @ApiModelProperty("人员ID") private int id; @ApiModelProperty("用户名") private String username; @ApiModelProperty("密码") private String password; @ApiModelProperty("年龄") private int age; @ApiModelProperty("手机号码") private int phone; @ApiModelProperty("邮箱") private String email; @ApiModelProperty("爱好") private String hobby; }
2 DAO层代码配置
Dao层首先我们编写一下Mapper配置文件:
PersonMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace=绑定一个指定的Dao/Mapper接口--> <mapper namespace="com.example.springboot.dao.PersonDao"> <select id="getPersonList" resultType="com.example.springboot.model.Person"> select * from person </select> <select id="getPersonById" parameterType="int" resultType="com.example.springboot.model.Person"> select * from person where id = #{id} </select> <select id="save" parameterType="com.example.springboot.model.Person"> insert into person (id,username,password,age,phone,email,hobby) values (#{id},#{username},#{password},#{age},#{phone},#{email},#{hobby}) </select> </mapper>
然后我们需要编写Dao代码
PersonDao.java
package com.example.springboot.dao; import com.example.springboot.model.Person; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; @Mapper @Repository public interface PersonDao { List<Person> getPersonList(); Person getPersonById(@Param("id")Integer id); void save(Person person); }
3 Service层代码配置
Service层比较简单,我们这里就单纯调用下Dao
PersonService.java
package com.example.springboot.service; import com.example.springboot.dao.PersonDao; import com.example.springboot.model.Person; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class PersonService { @Resource PersonDao personDao; public List<Person> getPersonList(){ return personDao.getPersonList(); } public Person getPersonById(Integer id){ return personDao.getPersonById(id); } public void save(Person person){ personDao.save(person); } }
4 Controller层代码配置
在Controller层我们需要编写请求入口以及给类和方法打上标识
PersonController.java
package com.example.springboot.controller; import com.example.springboot.model.Person; import com.example.springboot.service.PersonService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; @RestController @RequestMapping("/person") @Api(value = "人员接口", tags = "用户管理相关的接口") public class PersonController { @Resource PersonService personService; /** * 保存数据 * * @param person * @return */ @PostMapping(value = "/save") //说明是什么方法(可以理解为方法注释) @ApiOperation(value = "添加用户", notes = "添加用户") public String saveUser(@ApiParam("人员信息") Person person) { personService.save(person); return "保存成功"; } /** * 根据id查询用户 * * @param id * @return */ @GetMapping(value = "/findById") @ApiOperation(value = "根据id获取用户信息", notes = "根据id查询用户信息") public Person getUser(@ApiParam("用户ID") Integer id) { return personService.getPersonById(id); } /** * 获取用户集合 * * @param * @return */ @GetMapping(value = "/listPerson") @ApiOperation(value = "获取用户集合", notes = "获取用户集合") public List<Person> getPersonList() { return personService.getPersonList(); } }
6 请求测试
我们使用如下地址进行测试:http://localhost:8080/swagger-ui/index.html
,注意启动时我们要使用test或者dev环境进行启动;
当我们使用prod环境启动时
会报如下错误,源于我们上述的配置
1 获取用户列表
在Swagger我们可以轻松的进行接口测试,例如获取用户列表:
响应结果为:
2 依据ID获取指定用户
我们测试下通过id获取用户信息,请求为:
响应结果为:
3 新增用户
再测试下插入用户:
返回响应为:
检查数据库确实插入成功了
7 Model查看
我们还可以通过Schema查看Model信息:
8 其他皮肤配置
添加一个POM配置坐标,引入外部皮肤:
坐标如下:
<!-- 引入swagger-bootstrap-ui包 /doc.html--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency>
然后请求如下地址:http://localhost:8080/doc.html
可以选择到我们的接口,相当于一个皮肤布局吧
总结一下
今天用这篇Blog学习了如何用SpringBoot集成Swagger,其实工作中有发现过相关配置,但是之前一直不知道配置是如何实现的,今天总算解惑了,又有一批注解的含义解锁了,SpringBoot集成组件起来真的很方便,只需要简单引入starter,然后进行个人配置就行了,怪不得Spring生态现在这么强,Spring这种友好的集成模式可以让大家都能开发出基于一个工具的场景驱动器,然后和Spring很好的匹配,让Spring越来越丰富越来越强大。