Backend Development 10 min read

Implementing the Adapter Pattern for Multi‑Cloud OSS Storage in a Spring Boot Microservice

This article demonstrates how to use the Adapter pattern in a Spring Boot microservice to abstract multiple OSS providers such as Minio and Aliyun, including code implementations, configuration with Nacos, and a complete upload workflow with a FileService and controller.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Implementing the Adapter Pattern for Multi‑Cloud OSS Storage in a Spring Boot Microservice

In a microservice project where various OSS providers (Aliyun, Tencent Cloud, Minio, etc.) need to be supported, directly coupling controllers and services to a specific provider violates low‑coupling principles. The solution is to apply the Adapter pattern, defining a common interface for storage operations and providing concrete adapters for each provider.

Adapter Interface and Implementations

The StorageAdapter interface declares methods for creating buckets, uploading files, and retrieving URLs. Two concrete adapters are shown:

@Component
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
    public String getUrl(String bucket, String objectName) {
        return url + "/" + bucket + "/" + objectName;
    }
}
public class AliStorageAdapter implements StorageAdapter {
    @Override
    public void createBucket(String bucket) {
        System.out.println("aliyun");
    }
    @Override
    public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
        // implementation omitted
    }
    @Override
    public String getUrl(String bucket, String objectName) {
        return "aliyun";
    }
}

Both adapters implement the same interface, allowing the rest of the application to remain unchanged when switching providers.

Dynamic Adapter Selection with Nacos

The StorageConfig class reads the storage.service.type property from Nacos and returns the appropriate adapter bean. The bean and the configuration class are annotated with @RefreshScope so that changes in Nacos trigger a re‑initialisation without code modifications.

@Configuration
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("No matching storage adapter");
        }
    }
}

FileService and Controller

The domain‑level FileService depends on StorageAdapter and delegates bucket creation, file upload, and URL retrieval. The FileController exposes a /upload endpoint that validates input, calls the service, and returns the file URL.

@RestController
public class FileController {
    @Resource
    private FileService fileService;
    @PostMapping("/upload")
    public Result
upload(MultipartFile uploadFile, String bucket, String objectName) throws Exception {
        Preconditions.checkArgument(!ObjectUtils.isEmpty(uploadFile), "File cannot be empty");
        Preconditions.checkArgument(!StringUtils.isEmpty(bucket), "Bucket cannot be empty");
        String url = fileService.uploadFile(uploadFile, bucket, objectName);
        return Result.ok(url);
    }
}

Nacos Deployment and Configuration

The article also provides a Docker command to run Nacos in standalone mode, explains the meaning of each flag, and shows the Maven dependencies required for Nacos and Log4j2. The bootstrap.yml and the environment‑specific jc-club-oss-dev.yaml file illustrate how to externalise the storage.service.type property.

Testing Results

When storage.service.type is set to aliyun , the upload endpoint returns the string "aliyun". Switching the property to minio results in a successful image upload and a URL generated by Minio. Nacos logs indicate that configuration changes trigger a refresh event.

JavaMicroservicesNacosSpring BootFile UploadAdapter PatternOSS
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.