先上效果图:
不同版本的接口在不同的swagger版本分组中,我们通过在请求接口上添加自定义注解来对请求接口的版本分组;然后在构建 Docket 时,基于注解的值执行不同的处理逻辑,参考下面代码的 buildDocketWithGroupName
方法。
之前的版本配置是通过如下的代码的:
@Configuration
@EnableSwagger2
@EnableWebMvc
public class SwaggerConfig {
//默认版本的接口api-docs分组
@Bean
public Docket vDefault() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(buildApiInf())
.select()
.apis(RequestHandlerSelectors.basePackage("com.gysoft"))//controller路径
.paths(PathSelectors.any())
.build();
}
@Bean
public Docket vInfo210() {
return buildDocketWithGroupName(ApiVersionConstant.INFO_V210);
}
@Bean
public Docket vInfo220() {
return buildDocketWithGroupName(ApiVersionConstant.INFO_V220);
}
@Bean
public Docket vInfo230() {
return buildDocketWithGroupName(ApiVersionConstant.INFO_V230);
}
@Bean
public Docket v100() {
return buildDocketWithGroupName(ApiVersionConstant.INFO_V100);
}
@Bean
public Docket v201() {
return buildDocketWithGroupName(ApiVersionConstant.INFO_V201);
}
private ApiInfo buildApiInf() {
return new ApiInfoBuilder()
.title("接口列表")
.termsOfServiceUrl("http://127.0.0.1:8080/swagger-ui.html")
.description("springmvc swagger 接口测试")
.version("1.0.0")
.build();
}
private Docket buildDocketWithGroupName(String groupName) {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(buildApiInf())
.groupName(groupName)
.select()
.apis(input -> {
if (input.getHandlerMethod().hasMethodAnnotation(ApiVersion.class)) {
ApiVersion apiVersion = input.getHandlerMethod().getMethodAnnotation(ApiVersion.class);
if (apiVersion.group() != null && apiVersion.group().length != 0) {
if (Arrays.asList(apiVersion.group()).contains(groupName)) {
return true;
}
}
if (apiVersion.value() != null && apiVersion.value().length != 0) {
if (Arrays.asList(apiVersion.value()).contains(groupName)) {
return true;
}
}
}
ApiVersion clzzApiVersion = input.getHandlerMethod().getBeanType().getAnnotation(ApiVersion.class);
if (clzzApiVersion != null) {
if (clzzApiVersion.group() != null && clzzApiVersion.group().length != 0) {
if (Arrays.asList(clzzApiVersion.group()).contains(groupName)) {
return true;
}
}
if (clzzApiVersion.value() != null && clzzApiVersion.value().length != 0) {
if (Arrays.asList(clzzApiVersion.value()).contains(groupName)) {
return true;
}
}
}
return false;
})//controller路径
.paths(PathSelectors.any())
.build();
}
}
可以看出每在ApiVersionConstant
中增加一个版本(即一个常量值),我们需要在 Spring 中注册一个对应的 Docket
;这也太不方便了,既然 Spring 的 bean 可以在编程时动态注入,那么就能解决这个问题了。修改代码如下:
@Configuration
@EnableSwagger2
@EnableWebMvc
public class SwaggerConfig implements InitializingBean {
@Autowired
private ApplicationContext applicationContext;
//默认版本的接口api-docs分组
@Bean
public Docket vDefault() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(buildApiInf())
.select()
.apis(RequestHandlerSelectors.basePackage("com.gysoft"))//controller路径
.paths(PathSelectors.any())
.build();
}
private ApiInfo buildApiInf() {
return new ApiInfoBuilder()
.title("接口列表")
.termsOfServiceUrl("http://127.0.0.1:8080/swagger-ui.html")
.description("springmvc swagger 接口测试")
.version("1.0.0")
.build();
}
private Docket buildDocketWithGroupName(String groupName) {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(buildApiInf())
.groupName(groupName)
.select()
.apis(input -> {
if (input.getHandlerMethod().hasMethodAnnotation(ApiVersion.class)) {
ApiVersion apiVersion = input.getHandlerMethod().getMethodAnnotation(ApiVersion.class);
if (apiVersion.group() != null && apiVersion.group().length != 0) {
if (Arrays.asList(apiVersion.group()).contains(groupName)) {
return true;
}
}
if (apiVersion.value() != null && apiVersion.value().length != 0) {
if (Arrays.asList(apiVersion.value()).contains(groupName)) {
return true;
}
}
}
ApiVersion clzzApiVersion = input.getHandlerMethod().getBeanType().getAnnotation(ApiVersion.class);
if (clzzApiVersion != null) {
if (clzzApiVersion.group() != null && clzzApiVersion.group().length != 0) {
if (Arrays.asList(clzzApiVersion.group()).contains(groupName)) {
return true;
}
}
if (clzzApiVersion.value() != null && clzzApiVersion.value().length != 0) {
if (Arrays.asList(clzzApiVersion.value()).contains(groupName)) {
return true;
}
}
}
return false;
})//controller路径
.paths(PathSelectors.any())
.build();
}
@Override
public void afterPropertiesSet() throws Exception {
// 根据ApiConstantVersion构造 docket
Class<ApiVersionConstant> clzz = ApiVersionConstant.class;
Field[] declaredFields = clzz.getDeclaredFields();
// 动态注入bean
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
if(autowireCapableBeanFactory instanceof DefaultListableBeanFactory){
DefaultListableBeanFactory capableBeanFactory = (DefaultListableBeanFactory) autowireCapableBeanFactory;
for (Field declaredField : declaredFields) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().setFactoryMethodOnBean("buildDocketWithGroupName", "swaggerConfig")
.addConstructorArgValue(declaredField.get(ApiVersionConstant.class)).getBeanDefinition();
capableBeanFactory.registerBeanDefinition(declaredField.getName(), beanDefinition);
}
}
}
}
值得一提的是,我们这里通过注解来识别对应接口所属版本,具体实现参考方法:buildDocketWithGroupName。注解可以在 controller 上,也可以在它的方法上,方法上的优先级大于 controller上的版本注解优先级。注解在 controller 上默认当前类下的所有方法都属于这一版本。
下面是注解类的代码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface ApiVersion {
/**
* 接口版本号(对应swagger中的group)
*
* @return String[]
*/
String[] group() default {};
/**
* 接口版本号(对应swagger中的value)
*
* @return String[]
*/
String[] value() default {};
}
这里还有个优化可以做掉,那就是 apiInfo 应该针对不同版本使用不同的值,也就是 buildApiInf 方法。