越来越多的公司采用了前后端分离的开发模式来提高项目的开发效率,在前后端分离开发的前提是需要一份接口文档来进行接口对接,而swagger2作为接口文档的管理工具大大提高了我们的开发效率,通过在注解便能生成对应的接口文档,并能根据接口的修改进行实时更新,并且对接口进行统一管理.下面来学习下swagger2的使用吧.
本文是基于springboot基础上使用swagger2,首先是需要导入相应的jar包:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
可以在配置文件中指定环境来配置swagger,一般我们开发过程有三种环境,开发环境(test),开发环境(dev),正式环境(prod),以开发环境的配置文件(application_dev.yml)为例:
swagger2:
apiInfo:
hostUrl: localhost:${server.port}//访问路径
serverUrl: http://localhost:${server.port}//访问路径
show: true//是否启用swagger配置
接下来便是对swagger进行配置,创建一个配置类:
@Configuration//springboot中的自动配置,等价于在xml文件中进行配置
@EnableSwagger2
public class Swagger2 {
//@Value注解用于获取在配置文件中的信息
@Value("${swagger2.apiInfo.serverUrl}")
private String serverUrl;//访问路径
@Value("${swagger2.apiInfo.show}")
private boolean swagger2Show;//是否配置wagger,注意在正式环境使用swagger是会导致接口暴露,导致程序的安全性降低,所以只能在测试环境和开发环境中进行配置
@Value("${swagger2.apiInfo.hostUrl}")
private String hostUrl;//我们访问swagger接口文档是的url,需要拼接上'/swagger-ui.html'
@Bean//用@Bean标注方法等价于XML中配置bean。
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(swagger2Show)
.host(hostUrl)
.apiInfo(apiInfo())
.select()//select()函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现,可以采用扫描包的形式,扫面controller层下的所有接口,可以使用@ApiIgnore来隐藏不想暴露的接口
.apis(RequestHandlerSelectors.basePackage("扫描包的路径:"))
.paths(PathSelectors.any())
.build();
}
//用于展示一些接口文档的基本信息,来对接口文档进行说明,该信息会展示在接口文档上
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("接口文档名称")
.description("用于介绍该接口文档")
.termsOfServiceUrl(serverUrl)
.version("接口文档的版本号")
.build();
}
}
下面便是swagger的使用,首先是在实体类的使用,使用@ApiModel(description = "实体类说明")
注解来对实体类进行说明,使用@ApiModelProperty(value="说明")
注解来对实体类中的属性进行说明
@Data
@Entity
@Table(name ="关联数据库中表")
@EqualsAndHashCode
@ApiModel(description = "实体类说明")
public class Themes {
@ApiModelProperty(value = "创作人id")
@Column(name ="id" )
private Long id;
}
下面在接口上的使用:
@RestController
@RequestMapping("/api/theme")
@Api(value = "TestController ",description = "contrller层描述",tags = "TestController ")//swagger注解使用
public class TestController {
@Autowired
private DomeService domeService
@PostMapping
@ApiOperation(value = "接口描述")//swagger注解使用
//@ApiImplicitParams()注解用于需要说明的参数数量大于1的情况,当只有一个参数时使用@ApiImplicitParam();
@ApiImplicitParams({
@ApiImplicitParam(name = "type", value = "参数说明", required = true/*参数是否必须*/, dataType = "int"/*参数类型*/, paramType = "path"/*传输方式,具体百度*/),
@ApiImplicitParam(name = "id", value = "参数说明", required = true, dataType = "long", paramType = "path")
})
public viod testDome(@PathVariable("type") Integer type, @PathVariable("id") Long id){
System.out.println("hello world");
}
}
有时候我们会使用Map来传参时,需要对Map中的参数进行说明,此时我们需要自己构建一些注解来使用,以下代码参考网络资源,可直接复制使用:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonObject {
ApiJsonProperty[] value(); //对象属性值
String name(); //对象名称
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonProperty {
String key(); //key
String example() default "";
String type() default "string"; // 支持string、int、long、double
String description() default ""; //描述
boolean required() default true; //默认必传
}
import java.util.Map;
@Component
@Order //plugin加载顺序,默认是最后加载
public class MapApiReader implements ParameterBuilderPlugin {
@Autowired
private TypeResolver typeResolver;
@Override
public void apply(ParameterContext parameterContext) {
ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
if (methodParameter.getParameterType().canCreateSubtype(Map.class) || methodParameter.getParameterType().canCreateSubtype(String.class)) { //判断是否需要修改对象ModelRef,这里我判断的是Map类型和String类型需要重新修改ModelRef对象
Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class); //根据参数上的ApiJsonObject注解中的参数动态生成Class
if (optional.isPresent()) {
String name = optional.get().name(); //model 名称
ApiJsonProperty[] properties = optional.get().value();
parameterContext.getDocumentationContext().getAdditionalModels().add(typeResolver.resolve(createRefModel(properties, name))); //像documentContext的Models中添加我们新生成的Class
parameterContext.parameterBuilder() //修改Map参数的ModelRef为我们动态生成的class
.parameterType("body")
.modelRef(new ModelRef(name))
.name(name);
}
}
}
private final static String basePackage = "com.yunqian.shopping.domain.swagger."; //动态生成的Class名
/**
* 根据propertys中的值动态生成含有Swagger注解的javaBeen
*/
private Class createRefModel(ApiJsonProperty[] propertys, String name) {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass(basePackage + name);
try {
for (ApiJsonProperty property : propertys) {
ctClass.addField(createField(property, ctClass));
}
return ctClass.toClass();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据property的值生成含有swagger apiModelProperty注解的属性
*/
private CtField createField(ApiJsonProperty property, CtClass ctClass) throws NotFoundException, CannotCompileException {
CtField ctField = new CtField(getFieldType(property.type()), property.key(), ctClass);
ctField.setModifiers(Modifier.PUBLIC);
ConstPool constPool = ctClass.getClassFile().getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool);
ann.addMemberValue("value", new StringMemberValue(property.description(), constPool));
ann.addMemberValue("required", new BooleanMemberValue(property.required(), constPool));
attr.addAnnotation(ann);
ctField.getFieldInfo().addAttribute(attr);
return ctField;
}
private CtClass getFieldType(String type) throws NotFoundException {
CtClass fileType = null;
switch (type) {
case "string":
fileType = ClassPool.getDefault().get(String.class.getName());
break;
case "int":
fileType = ClassPool.getDefault().get(Integer.class.getName());
break;
case "long":
fileType = ClassPool.getDefault().get(Long.class.getName());
break;
case "double":
fileType = ClassPool.getDefault().get(Double.class.getName());
break;
}
return fileType;
}
@Override
public boolean supports(DocumentationType delimiter) {
return true;
}
}
此时在controller中可以使用以下注解方式:
@PostMapping("/pagination")
@ApiOperation(value = "分页查询主题列表")
public Page<Themes> pagination(@ApiJsonObject(name = "themeParams"/*注意:此处名称必须唯一,在接口文档中相当于创建一个实体类,该名称便为实体类名,名字重复无法打开swagger接口文档*/, value = {
@ApiJsonProperty(key = "customerId", description = "参数说明",type = "long"/*参数类型*/),
@ApiJsonProperty(key = "number", description = "页数",type = "int"),
@ApiJsonProperty(key = "size", description = "条数",type= "int")})
@RequestBody Map<String,Object> params){
searchHistoryService.saveHistory(params);
Page<Themes> page = themesService.pagination(params);
return page;
}
通过访问浏览器:localhost:8080/swagger-ui.html后,便是该页面
图1
网友评论