我们的所有微服务若想集成Swagger在线接口文档,都需要在各自模块中建立一个Swagger的配置类,关键代码如下:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
private static final String VERSION = "1.0.0";
/**
* 创建API
*/
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.enable(true)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
.paths(PathSelectors.any())
.build();
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("product-server接口文档")
.contact(new Contact("JAVA日知录","http://javadaily.cn","jianzh5@163.com"))
.description("product-server接口文档")
.version(VERSION)
.build();
}
}
复制代码
这样每个模块中都有一个 SwaggerConfig 配置类,他们的逻辑基本都一样,只是一些摘要信息不一样。这明显也算是违反了 DRY(Don't Repeat Yourself) 原则,这次我们来优化优化它,通过修改配置文件让Swagger自动配置。
不过在编写代码之前我们还是需要先了解一下SpringBoot的自动配置原理。
SpringBoot项目启动类上都会添加 @SpringBootApplication 注解,这个注解是个组合注解,他的核心功能是开启自动配置注解 @EnableAutoConfiguration ,如下图所示:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
。。。
}
复制代码
@EnableAutoConfiguration 又通过 @Import 注解导入了 AutoConfigurationImportSelector 。通过对 AutoConfigurationImportSelector 中 selectImports 方法的跟踪,我们找到SpringBoot启动时会通过 SpringFactoriesLoader.loadFactoryNames 方法 从 META-INF/spring.factories 这个文件下去寻找有没有自动配置类。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
复制代码
在项目中打开spring-boot-autoconfigure-2.1.9.RELEASE.jar,然后在 META-INF 文件夹下打开 spring.factories ,截取部分内容如下:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=/ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,/ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,/ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,/ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,/ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 复制代码
这个文件是一组的key=value的形式,通过value找到了自定义配置类,这里选取一个我们比较常见的配置类 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration ,打开源码:
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
...
}
复制代码
这里我们又发现配置类上使用了 @EnableConfigurationProperties({DataSourceProperties.class}) ,这个注解是去加载配置类。
再打开 DataSourceProperties.class ,代码如下:
@ConfigurationProperties(
prefix = "spring.datasource"
)
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private ClassLoader classLoader;
private String name;
private boolean generateUniqueName;
private Class<? extends DataSource> type;
private String driverClassName;
private String url;
private String username;
private String password;
...
}
复制代码
看到这里大家都应该很熟悉了,主要是通过注解 @ConfigurationProperties 从配置文件中加载spring.datasource开头的配置,如我们经常用的数据库配置
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://xxxxxxx/cloud_alibaba?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: xxx
password: xxxxxx
driver-class-name: com.mysql.jdbc.Driver
复制代码
从配置文件获取相关配置注入到DataSourceProperties这个类中
通过观察源码我们找到了SpringBoot自定义配置类的加载过程,主要是从 META-INF/spring.factories 找到对应的启动类,启动类上再通过配置类加载配置文件。说起来很简单,但是实现起来还是很复杂的。 接下来我们就根据我们的理解来完成Swagger的自动配置。
这里可能有人会问,虽然看完了自定义配置的加载逻辑,但是还是不会写怎么办?
不会写没关系啊,咱们不是会复制粘贴吗?
我们以 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 为例,开始我们的自定义配置(Copy,Paste)过程
我们在 common 模块建立 resources/META-INF/spring.factories 文件,粘贴上面的配置进行修改
org.springframework.boot.autoconfigure.EnableAutoConfiguration=/
com.javadaily.autoconfigure.SwaggerAutoConfiguration
复制代码
先想想我们需要哪些配置,一个 title ,一个 description ,还有一个 enable 用来控制是否开放在线测试,分析清楚了我们就建立对应的配置类 SwaggerProperties
@Data
@ConfigurationProperties(prefix = "javadaily.swagger")
public class SwaggerProperties {
/**
* 是否启用swagger,生产环境建议关闭
*/
private boolean enabled;
/**
* 文档标题
*/
private String title;
/**
* 文档描述
*/
private String description;
}
复制代码
核心代码,但是实现起来比较简单。
拷贝原来的配置类内容,加上相关注解,注入配置类,将原来写死的地方改成从配置类获取即可。
@Slf4j
@Configuration
//注入配置类
@EnableConfigurationProperties({SwaggerProperties.class})
//根据配置文件决定是否自动配置
@ConditionalOnProperty(prefix = "javadaily.swagger", name = "enabled", havingValue = "true")
@Import({Swagger2DocumentationConfiguration.class})
public class SwaggerConfig {
private static final String VERSION = "1.0.0";
private SwaggerProperties swaggerProperties;
public SwaggerAutoConfiguration (SwaggerProperties swaggerProperties){
this.swaggerProperties = swaggerProperties;
}
/**
* 创建API
*/
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.enable(true)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
.paths(PathSelectors.any())
.build();
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.contact(new Contact("JAVA日知录","http://javadaily.cn","jianzh5@163.com"))
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.version(VERSION)
.build();
}
}
复制代码
## swagger自定义配置属性
javadaily:
swagger:
enabled: true
title: account-service在线接口平台
description: account-service微服务相关接口
复制代码
经过以上四步我们完成了Swagger的自定义自动配置,接下来就是在服务层引入common模块,然后删除掉SwaggerConfig类,让common模块给我们自动配置。
是不是很简单呢,你们也来试试吧!
系列文章
SpringCloud 实战