SpringCloud Alibaba 微服务番外一 - Swagger 自定义自动配置

本贴最后更新于 1510 天前,其中的信息可能已经天翻地覆

概述

我们的所有微服务若想集成 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 自动配置原理

SpringBoot 项目启动类上都会添加 @SpringBootApplication 注解,这个注解是个组合注解,他的核心功能是开启自动配置注解 @EnableAutoConfiguration,如下图所示:
image.png

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
   。。。
}

@EnableAutoConfiguration 又通过 @Import 注解导入了 AutoConfigurationImportSelector。通过对 AutoConfigurationImportSelectorselectImports 方法的跟踪,我们找到 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.factories

在项目中打开 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}),这个注解是去加载配置类。

application.properties

再打开 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 的自动配置。

自定义 Swagger 自动配置

这里可能有人会问,虽然看完了自定义配置的加载逻辑,但是还是不会写怎么办?
不会写没关系啊,咱们不是会复制粘贴吗?
image.png

我们以 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 相关的配置

## swagger自定义配置属性
javadaily:
  swagger:
    enabled: true
    title: account-service在线接口平台
    description: account-service微服务相关接口

注意:这里配置文件需要以 javadaily.swagger 前缀开始,跟上面的配置类相对应

经过以上四步我们完成了 Swagger 的自定义自动配置,接下来就是在服务层引入 common 模块,然后删除掉 SwaggerConfig 类,让 common 模块给我们自动配置。

是不是很简单呢,你们也来试试吧!

  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    140 引用 • 441 回帖 • 1 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    941 引用 • 1458 回帖 • 150 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖 • 4 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...