spring boot 整合swagger
相信写接口文档是我们程序员最烦躁的一个环节,不知道大家是不是这样感觉的,不过俺是这样感觉的。所以想到了swagger来自动生成接口文档。特在此记录一下,有哪点不好的还请各位大佬勿喷。多多指教。话不多说。。。。。
首先我们的添加swagger所需要的jar包
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1-GA</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
<!-- swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.8.1</version>
</dependency>
下一步,创建SwaggerConfig配置
package com.shili.shwx.config.swagger;
import com.shili.shwx.config.FixedValueConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
/**
* @author chy
* @version 1.0
* @since 2020/2/14
* 配置swgger自动生成接口文档
*/
@Configuration
@EnableSwagger2
public class SwggerConfig {
@Value("${base.package:xxxx.xxx.xxx.controller}") //指定所需生成的接口路径(一般指定到controller路径下)
public String rootPackage;
@Bean
public Docket productApi() {
//**********************添加请求头参数(不需要添加请求头参数的请忽略)******************************
ParameterBuilder parameterBuilder = new ParameterBuilder();
ParameterBuilder parameterBuilder1 = new ParameterBuilder();
ParameterBuilder parameterBuilder2 = new ParameterBuilder();
ParameterBuilder parameterBuilder3 = new ParameterBuilder();
ArrayList<Parameter> parameters = new ArrayList<>();
parameterBuilder.name("").description("").modelRef(new ModelRef("")).parameterType("").required(true);
parameterBuilder1.name("platform").description("").modelRef(new ModelRef("Int")).parameterType("header").required(true);
parameterBuilder2.name("").description("").modelRef(new ModelRef("String")).parameterType("header").required(true);
parameterBuilder3.name("").description("").modelRef(new ModelRef("String")).parameterType("header").required(false);
parameters.add(parameterBuilder.build());
parameters.add(parameterBuilder1.build());
parameters.add(parameterBuilder2.build());
parameters.add(parameterBuilder3.build());
//******************************************请求头添加结束*************************************
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage(rootPackage))
.build()
.globalOperationParameters(parameters)
.apiInfo(metaData());
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title("微信小程序chy的接口文档")
.description("")
.version(FixedValueConfig.sysVersion)
.contact(new Contact("邮箱地址","blog.csdn.net","2645740054@qq.com"))
.license("My interface document")
.licenseUrl("http://localhost:0000/xxxx/swagger-ui.html#/")
.build();
}
}
到此swagger配置结束,在浏览器中
http://localhost:端口号/项目名/doc.html
打开这个路径就可以访问自动生成好的接口文档了,如下面展示:
注意有拦截器的请放行swagger相关的路径
//放行Swagger2页面
filterChainDefinitionMap.put("/swagger-ui.html","anon");
filterChainDefinitionMap.put("/swagger/**","anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/swagger-resources/**","anon");
filterChainDefinitionMap.put("/v2/**","anon");
还是比较简单的,不过有一点就是请求响应的返回格式是Map结构或者json格式的就不能详细的注解map结构或json格式里的字段,这点就很无奈了,当然也有相应的解决办法,解决的思路就是我们手动去告诉swagger返回的格式中有哪些字段,这些字段的解释是什么。如下就可以解决问题:
首选得自定义两个注解
/**
* @author chy
* @ClassName: ApiReturnJson
* @Description: 返回对象的定义 (描述这个类的作用)
* @version 1.0
* @since 2020/3/18
*/
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiReturnJson {
ApiReturnJsonPro[] value(); //对象属性值
String name(); //对象名称
}
/**
* @author chy
* @ClassName: ApiReturnJsonPro
* @Description: 每一个字段的定义备注说明 (描述这个类的作用)
* @version 1.0
* @since 2020/3/18
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiReturnJsonPro {
String key(); //key
String example() default "";
String type() default "string"; //支持string 和 int
String description() default "";
}
然后定义一个类继承 OperationModelsProviderPlugin
package com.shili.shwx.config.swagger;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.shili.shwx.annotation.ApiReturnJson;
import com.shili.shwx.annotation.ApiReturnJsonPro;
import com.shili.shwx.entity.ResultEntity;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationModelsProviderPlugin;
import springfox.documentation.spi.service.contexts.RequestMappingContext;
/**
* @author chy
* @version 1.0
* @since 2020/3/18
*/
@Component
@Order //plugin加载顺序,默认是最后加载
public class SwggerResult implements OperationModelsProviderPlugin {
@Autowired
private TypeResolver typeResolver;
private final static String basePackage = "com.xx.xx.xx.swagger.dto."; //动态生成的Class名(这个路径是你想要的存放生成好的虚拟dto)
@Override
public void apply(RequestMappingContext context) {
if (context.getReturnType().isInstanceOf(ResultEntity.class)) {
// 根据参数上的ApiJsonObject注解中的参数动态生成Class
Optional<ApiReturnJson> optional = context.findAnnotation(ApiReturnJson.class);
if(optional.isPresent()){
ApiReturnJson apiReturnJson = optional.get();
String name = apiReturnJson.name()+"_"+context.getName();
ApiReturnJsonPro[] properties = optional.get().value();
ResolvedType rt = typeResolver.resolve(createRefModel(properties, name));
// 像documentContext的Models中添加我们新生成的Class
context.getDocumentationContext().getAdditionalModels().add(rt);
context.operationModelsBuilder().addReturn(rt).build();
}
}
}
/**
* 根据propertys中的值动态生成含有Swagger注解的javaBeen
*/
private Class createRefModel(ApiReturnJsonPro[] propertys, String name) {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass(basePackage + name);
try {
for (ApiReturnJsonPro property : propertys) {
ctClass.addField(createField(property, ctClass));
}
return ctClass.toClass();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据property的值生成含有swagger apiModelProperty注解的属性
*/
private CtField createField(ApiReturnJsonPro 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));
if (ctField.getType().subclassOf(ClassPool.getDefault().get(String.class.getName())))
ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
if (ctField.getType().subclassOf(ClassPool.getDefault().get(Integer.class.getName())))
ann.addMemberValue("example", new IntegerMemberValue(Integer.parseInt(property.example()), 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;
}
return fileType;
}
@Override
public boolean supports(DocumentationType documentationType) {
return true;
}
}
在定义一个类 OperationBuilderPlugin
package com.shili.shwx.config.swagger;
import com.google.common.base.Optional;
import com.shili.shwx.annotation.ApiReturnJson;
import com.shili.shwx.entity.ResultEntity;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import java.util.HashSet;
import java.util.Set;
/**
* @author chy
* @version 1.0
* @since 2020/3/18
*/
@Component
@Order //plugin加载顺序,默认是最后加载
public class SwggerOperationBuilderPlugin implements OperationBuilderPlugin {
@Override
public void apply(OperationContext operationContext) {
// TODO Auto-generated method stub
if(operationContext.getReturnType().isInstanceOf(ResultEntity.class)) {
//根据参数上的ApiJsonObject注解中的参数动态生成Class
Optional<ApiReturnJson> optional = operationContext.findAnnotation(ApiReturnJson.class);
if(optional.isPresent()){
ApiReturnJson apiReturnJson = optional.get();
String name = apiReturnJson.name()+"_"+operationContext.getName();
Set<ResponseMessage> set = new HashSet<ResponseMessage>();
ModelRef mr = new ModelRef(name);
set.add(new ResponseMessage(200,"返回json用例说明",mr,null,null));
operationContext.operationBuilder().responseMessages(set);
}
}
}
@Override
public boolean supports(DocumentationType documentationType) {
return true;
}
}
最后就可以再接口中引用就完成了,接口引用实例:
/**
* 测试
* @return
*/
@ApiOperation(value = "测试一")
@RequestMapping(value = "test1",method = {RequestMethod.GET,RequestMethod.POST})
@ApiImplicitParams({
@ApiImplicitParam(name = "testId", value = "测试Id",dataType = "int", paramType = "query",required = true)
})
@ApiReturnJson(name = "PartriachCurriculaDetail",value = {
@ApiReturnJsonPro(key = "testName",example = "测试成功",description = "返回json格式中的字段testName的解释说明")
})
public ResultEntity partriachCurriculaDetail(Integer testId){
return new ResultEntity('n',"10002",{"testName":"测试成功"}",null,null);
}
到此整个spring boot 和swagger的整合完成,有不足的地方还请多多指教