工作上遇到一个实际问题,我的产品用到文件存储,文件存储有很多种,如:minio、oss(阿里)、CFS(腾讯)、fdfs。我们不知道客户会选择哪种配置,就需要我们的程序支持切换配置,我想到了数据源的切换,如果模仿着写了个starter。
1、创建一个工程,根据springboot规范,我们取名为store-spring-boot-starter,pom.xml依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>com.zhi.store</groupId> <artifactId>store-spring-boot-starter</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> </project>
2、定义文件操作接口并实现
/** * 文件操作接口 * * @author 张远志 * @since 2022年3月8日20:06:23 * */ public interface FileOperater { /** * 存储文件 * * @param input 文件输入流 * @param fileName 文件名 * @param contentType 文件类型 */ FileModel storeFile(InputStream input, String fileName, String contentType); /** * 文件下载 * * @param output 文件输出流 * @param filePath 文件路径 */ void downFile(OutputStream output, String filePath); /** * 删除文件 * * @param filePath 文件路径 */ void deleteFile(String filePath); /** * 获取文件上传地址 * * @param fileName 文件名 * @return */ String generateUploadUrl(String fileName); /** * 获取文件下载地址 * * @param filePath 文件路径 * @return */ String generateDownloadUrl(String filePath); }
具体实现类MinoFileOperater和AliOssFileOperater略
3、定义自动装配类和属性定义类
StroeAutoConfiguration:
@Configuration @EnableConfigurationProperties(StoreProperties.class) @ConditionalOnProperty(prefix = StoreProperties.prefix, name = { "enabled" }, havingValue = "true", matchIfMissing = true) @Import({ StoreConfiguration.Minio.class, StoreConfiguration.AliSSO.class }) public class StroeAutoConfiguration { }
StoreConfiguration:
public class StoreConfiguration { @Configuration @ConditionalOnClass(MinioClient.class) @ConditionalOnMissingBean(FileOperater.class) @ConditionalOnProperty(name = StoreProperties.prefix + ".storeType", havingValue = "minio", matchIfMissing = true) static class Minio { @Bean public MinioFileOperater fileOperater(StoreProperties properties) { MinioFileOperater fileOperater = new MinioFileOperater(properties); return fileOperater; } } @Configuration @ConditionalOnClass(OSS.class) @ConditionalOnMissingBean(FileOperater.class) @ConditionalOnProperty(name = StoreProperties.prefix + ".storeType", havingValue = "alioss", matchIfMissing = true) static class AliSSO { @Bean public AliOssFileOperater fileOperater(StoreProperties properties) { AliOssFileOperater fileOperater = new AliOssFileOperater(properties); return fileOperater; } } }
StoreProperties:
@ConfigurationProperties(prefix = StoreProperties.prefix) public class StoreProperties { public static final String prefix = "zhi.store"; /** * 是否开启,默认开启 */ private boolean enabled; /** * 存储类型 */ private String storeType; /** * 存储地址 */ private String endpoint; private String accessKey; private String secretKey; /** * 默认存储桶 */ private String bucket; /** * 文件是否重命名 */ private boolean rename; }
4、在resources/META-INF目录创建文件spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zhi.store.StroeAutoConfiguration
5、生成spring-configuration-metadata.json文件,定义属性元数据
属性类中存在@ConfigurationProperties注解时,执行mvn package生成的jar包会自动添加spring-configuration-metadata.json文件,效果如下: