SpringBoot集成Swagger => springfox,两个jar包
-
Springfox-swagger2
-
swagger-swagger-ui
添加Maven依赖
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
使用Swagger,我们需要编写一个配置类-SwaggerConfig来配置 Swagger
package com.example.blog.cofig;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.GetMapping;
import springfox.documentation.builders.BuilderDefaults;
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;
import java.util.ArrayList;
@Configuration
@EnableSwagger2 //开启swagger2
@EnableKnife4j
public class SwaggerConfig {
@Bean
public Docket docket1() {
return new Docket(DocumentationType.SWAGGER_2).groupName("用户1");
}
@Bean
public Docket docket2() {
return new Docket(DocumentationType.SWAGGER_2).groupName("用户2");
}
@Bean
public Docket docket3() {
return new Docket(DocumentationType.SWAGGER_2).groupName("用户3");
}
//配置了swagger的docket的bean实例
@Bean
public Docket docket(Environment environment) {
//设置要显示的swagger2环境
Profiles profiles = Profiles.of("dev");
//通过environment.acceptsProfiles(profiles)判断是否处在自己设定的环境中
//如果环境是dev,那就是true,如果是prod就是false
boolean environmentFlag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("苏七")
.enable(environmentFlag) //是否启用swagger2
.select() //通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
/*RequestHandlerSelectors类中的方法
basePackages:指定要扫描的包
any:扫描全部
none:什么都不扫
withMethodAnnotation:扫描方法上的注解,比如
RequestHandlerSelectors.withMethodAnnotation(GetMapping.class)扫描注解带有GetMapping的handler
扫描类上的注解
RequestHandlerSelectors.withClassAnnotation(Controller.class)扫描注解带有GetMapping的handler
* */
.apis(RequestHandlerSelectors.basePackage("com.example.blog.controller"))
.build();
}
//配置apiInfo
public ApiInfo apiInfo() {
/*作者信息*/
Contact contact = new Contact("苏七", "http://blog.suqiqaq.cn", "1141793961@qq.com");
return new ApiInfo("blog-api Documentation",
"博客前端文档",
"v1.0",
"http://blog.suqiqaq.cn",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
点击docket,可以看到swagger2只能通过构造方法进行配置,因为没有set方法,并且主要设置apiInfo
package springfox.documentation.spring.web.plugins;
public class Docket implements DocumentationPlugin {
public static final String DEFAULT_GROUP_NAME = "default";
private final DocumentationType documentationType;
private final List<SecurityContext> securityContexts = Lists.newArrayList();
private final Map<RequestMethod, List<ResponseMessage>> responseMessages = Maps.newHashMap();
private final List<Parameter> globalOperationParameters = Lists.newArrayList();
private final List<Function<TypeResolver, AlternateTypeRule>> ruleBuilders = Lists.newArrayList();
private final Set<Class> ignorableParameterTypes = Sets.newHashSet();
private final Set<String> protocols = Sets.newHashSet();
private final Set<String> produces = Sets.newHashSet();
private final Set<String> consumes = Sets.newHashSet();
private final Set<ResolvedType> additionalModels = Sets.newHashSet();
private final Set<Tag> tags = Sets.newHashSet();
private PathProvider pathProvider;
private List<? extends SecurityScheme> securitySchemes;
private Ordering<ApiListingReference> apiListingReferenceOrdering;
private Ordering<ApiDescription> apiDescriptionOrdering;
private Ordering<Operation> operationOrdering;
private ApiInfo apiInfo;
private String groupName;
private boolean enabled;
private GenericTypeNamingStrategy genericsNamingStrategy;
private boolean applyDefaultResponseMessages;
private String host;
private Optional<String> pathMapping;
private ApiSelector apiSelector;
private boolean enableUrlTemplating;
private List<VendorExtension> vendorExtensions;
public Docket(DocumentationType documentationType) {
this.apiInfo = ApiInfo.DEFAULT;
this.groupName = "default";
this.enabled = true;
this.genericsNamingStrategy = new DefaultGenericTypeNamingStrategy();
this.applyDefaultResponseMessages = true;
this.host = "";
this.pathMapping = Optional.absent();
this.apiSelector = ApiSelector.DEFAULT;
this.enableUrlTemplating = false;
this.vendorExtensions = Lists.newArrayList();
this.documentationType = documentationType;
}
public Docket apiInfo(ApiInfo apiInfo) {
this.apiInfo = (ApiInfo)BuilderDefaults.defaultIfAbsent(apiInfo, apiInfo);
return this;
}
...
}
再点击apiInfo,将最下面的默认info信息复制,新建一个函数返回该信息,也就是上面SwaggerConfig的配置信息
package springfox.documentation.service;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ApiInfo {
public static final Contact DEFAULT_CONTACT = new Contact("", "", "");
public static final ApiInfo DEFAULT;
private final String version;
private final String title;
private final String description;
private final String termsOfServiceUrl;
private final String license;
private final String licenseUrl;
private final Contact contact;
private final List<VendorExtension> vendorExtensions;
/** @deprecated */
@Deprecated
public ApiInfo(String title, String description, String version, String termsOfServiceUrl, String contactName, String license, String licenseUrl) {
this(title, description, version, termsOfServiceUrl, new Contact(contactName, "", ""), license, licenseUrl, new ArrayList());
}
public ApiInfo(String title, String description, String version, String termsOfServiceUrl, Contact contact, String license, String licenseUrl, Collection<VendorExtension> vendorExtensions) {
this.title = title;
this.description = description;
this.version = version;
this.termsOfServiceUrl = termsOfServiceUrl;
this.contact = contact;
this.license = license;
this.licenseUrl = licenseUrl;
this.vendorExtensions = Lists.newArrayList(vendorExtensions);
}
public String getTitle() {
return this.title;
}
public String getDescription() {
return this.description;
}
public String getTermsOfServiceUrl() {
return this.termsOfServiceUrl;
}
public Contact getContact() {
return this.contact;
}
public String getLicense() {
return this.license;
}
public String getLicenseUrl() {
return this.licenseUrl;
}
public String getVersion() {
return this.version;
}
public List<VendorExtension> getVendorExtensions() {
return this.vendorExtensions;
}
static {
DEFAULT = new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos", DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList());
}
}
访问测试 :http://localhost:8777/swagger-ui.html ,可以看到swagger的界面
swagger配置扫描接口
构建Docket时通过select()方法配置怎么扫描接口。
package com.example.blog.cofig;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.GetMapping;
import springfox.documentation.builders.BuilderDefaults;
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;
import java.util.ArrayList;
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig
{
//配置了swagger的docket的bean实例
@Bean
public Docket docket()
{
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select() //通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
/*RequestHandlerSelectors类中的方法
basePackages:指定要扫描的包
any:扫描全部
none:什么都不扫
withMethodAnnotation:扫描方法上的注解,比如
RequestHandlerSelectors.withMethodAnnotation(GetMapping.class)扫描注解带有GetMapping的handler
扫描类上的注解
RequestHandlerSelectors.withClassAnnotation(Controller.class)扫描注解带有GetMapping的handler
* */
.apis(RequestHandlerSelectors.basePackage("com.example.blog.controller"))
.build();
}
//配置apiInfo
public ApiInfo apiInfo()
{
/*作者信息*/
Contact contact = new Contact("苏七","http://8.136.84.238:8080","1141793961@qq.com");
return new ApiInfo("blog-api Documentation",
"博客前端文档",
"v1.0",
"http://8.136.84.238:8080",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
Docket类中有一个enable方法,.enable可以配置是否自动开启,默认为true
Docket类中有一个select方法
public ApiSelectorBuilder select() {
return new ApiSelectorBuilder(this);
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package springfox.documentation.spring.web.plugins;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import springfox.documentation.RequestHandler;
import springfox.documentation.spi.service.contexts.ApiSelector;
public class ApiSelectorBuilder {
private final Docket parent;
private Predicate<RequestHandler> requestHandlerSelector;
private Predicate<String> pathSelector;
public ApiSelectorBuilder(Docket parent) {
this.requestHandlerSelector = ApiSelector.DEFAULT.getRequestHandlerSelector();
this.pathSelector = ApiSelector.DEFAULT.getPathSelector();
this.parent = parent;
}
public ApiSelectorBuilder apis(Predicate<RequestHandler> selector) {
this.requestHandlerSelector = Predicates.and(this.requestHandlerSelector, selector);
return this;
}
public ApiSelectorBuilder paths(Predicate<String> selector) {
this.pathSelector = Predicates.and(this.pathSelector, selector);
return this;
}
public Docket build() {
return this.parent.selector(new ApiSelector(this.combine(this.requestHandlerSelector, this.pathSelector), this.pathSelector));
}
private Predicate<RequestHandler> combine(Predicate<RequestHandler> requestHandlerSelector, Predicate<String> pathSelector) {
return Predicates.and(requestHandlerSelector, this.transform(pathSelector));
}
private Predicate<RequestHandler> transform(final Predicate<String> pathSelector) {
return new Predicate<RequestHandler>() {
public boolean apply(RequestHandler input) {
return Iterables.any(input.getPatternsCondition().getPatterns(), pathSelector);
}
};
}
}
里面的apis就是指定扫描的路径
paths是一个过滤器,只扫描哪些接口
这里的可选值还有
any() // 任何请求都扫描
none() // 任何请求都不扫描
regex(final String pathRegex) // 通过正
则表达式控制
ant(final String antPattern) // 通过ant()控制
动态配置当项目处于测试、开发环境时显示swagger,处于生产环境时不显示
新建三个application.yaml文件
在application.yaml设置active: dev,就是处于开发环境下
spring:
profiles:
active: dev
application-dev.yaml:
server:
port: 8777
application-prod.yaml:
server:
port: 8776
package com.example.blog.cofig;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.GetMapping;
import springfox.documentation.builders.BuilderDefaults;
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;
import java.util.ArrayList;
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig
{
//配置了swagger的docket的bean实例
@Bean
public Docket docket(Environment environment)
{
//设置要显示的swagger2环境
Profiles profiles = Profiles.of("dev","prod");
//通过environment.acceptsProfiles(profiles)判断是否处在自己设定的环境中
//如果环境是dev,那就是true,如果是prod就是false
boolean environmentFlag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(environmentFlag) //是否启用swagger2
.select() //通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
/*RequestHandlerSelectors类中的方法
basePackages:指定要扫描的包
any:扫描全部
none:什么都不扫
withMethodAnnotation:扫描方法上的注解,比如
RequestHandlerSelectors.withMethodAnnotation(GetMapping.class)扫描注解带有GetMapping的handler
扫描类上的注解
RequestHandlerSelectors.withClassAnnotation(Controller.class)扫描注解带有GetMapping的handler
* */
.apis(RequestHandlerSelectors.basePackage("com.example.blog.controller"))
.build();
}
//配置apiInfo
public ApiInfo apiInfo()
{
/*作者信息*/
Contact contact = new Contact("苏七","http://8.136.84.238:8080","1141793961@qq.com");
return new ApiInfo("blog-api Documentation",
"博客前端文档",
"v1.0",
"http://8.136.84.238:8080",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
所以只有在dev下,swagger2才会开启
配置API分组
目的:每个人配置自己的分组,就可以修改自己的接口文档,也就可以协同开发了
1、如果没有配置分组,默认是default。通过groupName()方法即可配置分组:
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.groupName("苏七") // 配置分组
//....
}
2、重启项目查看分组
3、如何配置多个分组?配置多个分组只需要配置多个docket即可:
package com.example.blog.cofig;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.GetMapping;
import springfox.documentation.builders.BuilderDefaults;
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;
import java.util.ArrayList;
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig
{
@Bean
public Docket docket1()
{
return new Docket(DocumentationType.SWAGGER_2).groupName("用户1");
}
@Bean
public Docket docket2()
{
return new Docket(DocumentationType.SWAGGER_2).groupName("用户2");
}
@Bean
public Docket docket3()
{
return new Docket(DocumentationType.SWAGGER_2).groupName("用户3");
}
//配置了swagger的docket的bean实例
@Bean
public Docket docket(Environment environment)
{
//设置要显示的swagger2环境
Profiles profiles = Profiles.of("dev","prod");
//通过environment.acceptsProfiles(profiles)判断是否处在自己设定的环境中
//如果环境是dev,那就是true,如果是prod就是false
boolean environmentFlag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("苏七")
.enable(environmentFlag) //是否启用swagger2
.select() //通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
/*RequestHandlerSelectors类中的方法
basePackages:指定要扫描的包
any:扫描全部
none:什么都不扫
withMethodAnnotation:扫描方法上的注解,比如
RequestHandlerSelectors.withMethodAnnotation(GetMapping.class)扫描注解带有GetMapping的handler
扫描类上的注解
RequestHandlerSelectors.withClassAnnotation(Controller.class)扫描注解带有GetMapping的handler
* */
.apis(RequestHandlerSelectors.basePackage("com.example.blog.controller"))
.build();
}
//配置apiInfo
public ApiInfo apiInfo()
{
/*作者信息*/
Contact contact = new Contact("苏七","http://8.136.84.238:8080","1141793961@qq.com");
return new ApiInfo("blog-api Documentation",
"博客前端文档",
"v1.0",
"http://8.136.84.238:8080",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
刷新页面,就可以在右上角查看自己分组
实体类配置
1.给实体类添加注解
@ApiModel("发布文章前端所传递的参数")
@Data
public class ArticleParam
{
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
private ArticleBodyParam body;
private CategoryVo category;
@ApiModelProperty("文章摘要")
private String summary;
private List<TagVo> tags;/*文章标签*/
private String title;
}
2.实体在请求接口的返回值上(即使是泛型),都能映射到实体项中:
/**
* 首页 文章列表
* @param pageParams
* @return
*/
@PostMapping
@Cache(expire = 5 *60 * 1000,name = "list_article")
public Result listArticle(@RequestBody PageParams pageParams)
{
return articleService.listArticle(pageParams);
}
3、重启查看测试
注:并不是因为@ApiModel这个注解让实体显示在这里了,而是只要出现在接口方法的返回值上的实体都会显示在这里,而@ApiModel和@ApiModelProperty这两个注解只是为实体添加注释的。
@ApiModel为类添加注释
@ApiModelProperty为类属性添加注释
常用注解
Swagger注解 | 简单说明 |
---|---|
@Api(tags = "xxx模块说明") | 作用在模块类上 |
@ApiOperation("xxx接口说明") | 作用在接口方法上 |
@ApiModel("xxxPOJO说明") | 作用在模型类上:如VO、BO |
@ApiModelProperty(value = "xxx属性说明",hidden = true) | 作用在类方法和属性上,hidden设置为true可以隐藏该属性 |
@ApiParam("xxx参数说明") | 作用在参数、方法和字段上,类似@ApiModelProperty |
我们也可以给请求的接口配置一些注释
/**
* 首页 文章列表
* @param pageParams
* @return
*/
@PostMapping
@Cache(expire = 5 *60 * 1000,name = "list_article")
@ApiOperation("文章列表接口")
public Result listArticle(@ApiParam("前端传递的参数") @RequestBody PageParams pageParams)
{
return articleService.listArticle(pageParams);
}
这样的话,可以给一些比较难理解的属性或者接口,增加一些配置信息,让人更容易阅读!
相较于传统的Postman或Curl方式测试接口,使用swagger简直就是傻瓜式操作,不需要额外说明文档(写得好本身就是文档)而且更不容易出错,只需要录入数据然后点击Execute,如果再配合自动化框架,可以说基本就不需要人为操作了。
Swagger是个优秀的工具,现在国内已经有很多的中小型互联网公司都在使用它,相较于传统的要先出Word接口文档再测试的方式,显然这样也更符合现在的快速迭代开发行情。当然了,提醒下大家在正式环境要记得关闭Swagger,一来出于安全考虑二来也可以节省运行时内存。
拓展:其他皮肤
我们可以导入不同的包实现不同的皮肤定义:
bootstrap-ui 访问 http://localhost:8777/doc.html
<!-- 引入swagger-bootstrap-ui包 /doc.html-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.1</version>
</dependency>
Layui-ui 访问 http://localhost:8777/docs.html
<dependency>
<groupId>com.github.caspar-chen</groupId>
<artifactId>swagger-ui-layer</artifactId>
<version>1.1.3</version>
</dependency>
mg-ui 访问 http://localhost:8777/document.html
<!-- 引入swagger-ui-layer包 /document.html-->
<dependency>
<groupId>com.zyplayer</groupId>
<artifactId>swagger-mg-ui</artifactId>
<version>1.0.6</version>
</dependency>
Knife4j 访问 http://localhost:8777/doc.html
<!--整合Knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
-
在Swagger2Config中增加一个@EnableKnife4j注解,该注解可以开启knife4j的增强功能;
-
@Configuration @EnableSwagger2 //开启swagger2 @EnableKnife4j public class SwaggerConfig {...}
knife4j部署到服务器
将后端项目重新打包,上传到服务器
由于nginx代理了后端端口,所以直接在网站端口上加上/api/doc.html,也就是
http://域名/api/doc.html即可