ServiceLoader是JDK自带的轻量SPI(服务提供者接口)实现,无需引入额外依赖,最适合入门级档案管理的可插拔扩展。
直接使用官方Spring Initializr生成,操作路径:
在src/main/java/com/example/archivemanager下新建spi包,创建ArchiveStorage接口:
```java package com.example.archivemanager.spi; import com.example.archivemanager.entity.Archive; import java.util.List; // 定义档案存储的通用接口,后续不同存储(本地、OSS、MinIO)只需要实现它 public interface ArchiveStorage { // 每个实现类必须返回唯一标识,用于路由 String getStorageType(); // 保存档案 boolean saveArchive(Archive archive); // 根据ID查询档案 Archive getArchiveById(String id); // 查询所有档案 List再在entity包创建简单的档案实体类:
```java package com.example.archivemanager.entity; public class Archive { private String id; private String name; private String content; // 全参、无参构造方法,getter、setter(IDEA右键Generate自动生成) public Archive() {} public Archive(String id, String name, String content) { this.id = id; this.name = name; this.content = content; } // 生成getter、setter:id、name、content public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ```在spi包下新建impl子包,创建LocalArchiveStorage类:
```java package com.example.archivemanager.spi.impl; import com.example.archivemanager.entity.Archive; import com.example.archivemanager.spi.ArchiveStorage; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; // 必须加@Component,让Spring Boot接管实例(可选,但简化接口调用) @Component public class LocalArchiveStorage implements ArchiveStorage { // 本地模拟存储用ConcurrentHashMap保证线程安全 private static final Map在config包下创建SpiConfig类:
```java package com.example.archivemanager.config; import com.example.archivemanager.spi.ArchiveStorage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; @Configuration public class SpiConfig { // 生成一个Bean:key是storageType,value是ArchiveStorage实例 @Bean public Map在src/main/resources下新建META-INF/services目录,创建名为com.example.archivemanager.spi.ArchiveStorage的文件,内容:
``` com.example.archivemanager.spi.impl.LocalArchiveStorage ```
在service包下创建ArchiveService类:
```java package com.example.archivemanager.service; import com.example.archivemanager.config.SpiConfig; import com.example.archivemanager.entity.Archive; import com.example.archivemanager.spi.ArchiveStorage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.UUID; @Service public class ArchiveService { @Autowired private Map在controller包下创建ArchiveController类:
```java package com.example.archivemanager.controller; import com.example.archivemanager.entity.Archive; import com.example.archivemanager.service.ArchiveService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.; import java.util.List; @RestController @RequestMapping("/archive") public class ArchiveController { @Autowired private ArchiveService archiveService; // 保存:POST /archive/save?storageType=local&name=测试&content=测试内容 @PostMapping("/save") public boolean save(@RequestParam(required = false) String storageType, @RequestParam String name, @RequestParam String content) { return archiveService.saveArchive(storageType, name, content); } // 查询单个:GET /archive/get?storageType=local&id=xxx @GetMapping("/get") public Archive get(@RequestParam(required = false) String storageType, @RequestParam String id) { return archiveService.getArchiveById(storageType, id); } // 查询所有:GET /archive/list?storageType=local @GetMapping("/list") public Listcurl -X POST "http://localhost:8080/archive/save?name=入职档案&content=2024年入职",返回truecurl "http://localhost:8080/archive/list",返回刚才的入职档案这里的零代码修改指的是无需修改主工程的代码,只需要新建一个独立的Maven模块或者JAR包作为扩展。
在IDEA主工程上右键→New→Module→Maven,Group填com.example,Artifact填archive-minio-storage,点击Create。
将主工程作为依赖引入,同时添加MinIO的依赖:
```xml在扩展模块的src/main/java/com/example/archiveminio下创建MinioArchiveStorage类:
```java package com.example.archiveminio; import com.example.archivemanager.entity.Archive; import com.example.archivemanager.spi.ArchiveStorage; import io.minio.BucketExistsArgs; import io.minio.MakeBucketArgs; import io.minio.MinioClient; import io.minio.PutObjectArgs; import io.minio.GetObjectArgs; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Component public class MinioArchiveStorage implements ArchiveStorage { private static final String BUCKET_NAME = "archive-bucket"; private static final Map在扩展模块的src/main/resources下新建META-INF/services目录,创建名为com.example.archivemanager.spi.ArchiveStorage的文件,内容:
``` com.example.archiveminio.MinioArchiveStorage ```docker run -d -p 9000:9000 -p 9001:9001 --name minio -e "MINIO_ROOT_USER=minioadmin" -e "MINIO_ROOT_PASSWORD=minioadmin" minio/minio server /data --console-address ":9001"curl -X POST "http://localhost:8080/archive/save?storageType=minio&name=离职档案&content=2025年离职",返回truecurl "http://localhost:8080/archive/get?storageType=minio&id=刚才返回的UUID(可从list接口取)"