[纯干货]SpringCould + 适配器模式 + nacos动态部署 OSS 对接

一、前言

在一个微服务项目里,我们的 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);
    }

这样适配器模式就搭建完成啦,小伙伴们快去试试吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值