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.
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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.