一、前言
在一个微服务项目里,我们的 OSS 云存储服务常常需要配置诸如阿里云、腾讯云、minio 等多个云存储厂商的业务代码,而且后续无法确保是否会增添新的云存储厂商。此时,倘若我们要修改具体使用的云存储厂商,就会致使 controller 层和 service 层发生变动,这并不符合低耦合的理念。在这种情况下,我们完全可以采用适配器模式来开展项目开发!
比如:假设我们最初使用的是阿里云的云存储服务,后续要切换为腾讯云。如果没有采用适配器模式,那么在修改配置时,就会直接抛出 erro 99+,需要在 controller 层和 service 层大量修改代码,涉及到与云存储交互的逻辑都需要重新调整。而通过适配器模式,我们可以将不同云存储厂商的接口适配为统一的接口,这样在切换时,只需修改少量的配置或者适配器的实现,大大降低了代码的改动范围和维护成本。
这里我的项目以minio + aliyun为例,结合nacos动态部署+适配器模式完成。
二、适配器模式
适配器模式我们可以理解为一个桥梁,是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
举一个很贴切的例子,在中国,家用电的电压是220V,但是在美国,他们的家用电压为110V,因此如果我们要想在国外也能达到国内一样的充电体验,我们可能需要购买一个适配器,来将110V电压转换为220V,那么这个案例就是一个简单的适配器模式
三、项目实战
我们先新建一个文件存储的适配器 (Adapter),这个适配器我们创建了一些基础的方法,例如上传下载、查询等等
/**
* 文件存储适配器
*/
public interface StorageAdapter {
/**
* 创建bucket桶
*/
void createBucket(String bucket);
/**
* 上传文件
*/
void uploadFile(MultipartFile multipartFile, String bucket, String objectName);
/**
* 列出所有桶
*/
List<String> getAllBucket();
/**
* 列出当前桶及文件
*/
List<FileInfo> getAllFile(String bucket);
/**
* 下载文件
*/
InputStream downLoad(String bucket, String objectName);
/**
* 删除桶
*/
void deleteBucket(String bucket) ;
}
然后我们需要简历对应的云存储实现类去实现适配器,并且重写对应对应方法
/**
* minio文件存储适配器
*/
public class MinioStorageAdapter implements StorageAdapter{
@Resource
private MinioUtil minioUtil;
@Value("${minio.url}")
private String url;
@Override
@SneakyThrows
public void createBucket(String bucket) {
minioUtil.createBucket(bucket);
}
/**
* 下载文件
*/
@Override
@SneakyThrows
public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
minioUtil.createBucket(bucket);
if ( objectName != null ){
minioUtil.uploadFile(multipartFile.getInputStream(),bucket,objectName + "/" + multipartFile.getOriginalFilename());
}else{
minioUtil.uploadFile(multipartFile.getInputStream(),bucket,multipartFile.getOriginalFilename());
}
}
@Override
@SneakyThrows
public List<String> getAllBucket() {
return minioUtil.getAllBucket();
}
@Override
@SneakyThrows
public List<FileInfo> getAllFile(String bucket) {
return minioUtil.getAllFile(bucket);
}
@Override
@SneakyThrows
public InputStream downLoad(String bucket, String objectName) {
return minioUtil.downLoad(bucket,objectName);
}
@Override
@SneakyThrows
public void deleteBucket(String bucket) {
minioUtil.deleteBucket(bucket);
}
}
这里我们在实现一个阿里云的接口,等会就用minio+阿里云进行测试,这里只用作测试,所以很多方法都没实现
/**
* 阿里文件存储适配器
*/
public class AliStorageAdapter implements StorageAdapter{
@Override
public void createBucket(String bucket) {
}
@Override
public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
}
@Override
public List<String> getAllBucket() {
List<String> bucketNameList = new LinkedList<>();
bucketNameList.add("aliyun");
return bucketNameList;
}
@Override
public List<FileInfo> getAllFile(String bucket) {
return null;
}
@Override
public InputStream downLoad(String bucket, String objectName) {
return null;
}
@Override
public void deleteBucket(String bucket) {
}
}
然后就来到一个问题,就是怎么分辨到底是用aliyun的oss还是minio的呢?
这里我们就要创建一个配置类,在配置类中使用Value属性去读取配置的oss对象,然后通过@RefreshScope注解可以动态刷新bean,然后进行判断,满足条件的就new一个对象
/**
* 适配器config
*/
@Configuration
@RefreshScope
@EnableAutoConfiguration
public class StorageConfig {
@Value("${storage.service.type}")
private String storageType;
@Bean
@RefreshScope
public StorageAdapter storageAdapter(){
if ("minio".equals(storageType)){
return new MinioStorageAdapter();
} else if ("aliyun".equals(storageType)) {
return new AliStorageAdapter();
}else {
throw new IllegalArgumentException("未找到对应的文件存储服务器");
}
}
}
注:这里我是用的是动态部署,所以我的{storage.service.type}字段是配置在nacos上的,所以我使用@RefreshScope可以做到不重启项目去动态的更改配置,动态刷新
然后我们在 fileservice 层就可以使用我们的适配器模式进行业务方法的实现。
@Service
public class FileService {
private final StorageAdapter storageAdapter;
public FileService(StorageAdapter storageAdapter){
this.storageAdapter = storageAdapter;
}
/**
* 列出所有桶
*/
public List<String> getAllBucket() {
return storageAdapter.getAllBucket();
}
在 controller 我们也是调用 fileservice 方法
@RestController
public class FileController {
@Resource
private MinioUtil minioUtil;
@Value("${storage.service.type}")
private String storageType;
@Resource
private FileService fileService;
@RequestMapping("/testGetAllBuckets")
public String testGetAllBuckets() throws Exception {
List<String> bucket = fileService.getAllBucket();
return bucket.get(0);
}
这样适配器模式就搭建完成啦,小伙伴们快去试试吧!
标签:String,SpringCould,适配器,bucket,nacos,minioUtil,Override,public From: https://blog.csdn.net/indexqian/article/details/141098584