网站首页/ 信息中心/ 档案百科/

从零搭轻量可插拔档案管理软件:掌握易扩展核心三步

发布时间:2026年06月30日 12:40:13 浏览量:0

第一步:搭建基础可扩展架构(使用Spring Boot + ServiceLoader)

ServiceLoader是JDK自带的轻量SPI(服务提供者接口)实现,无需引入额外依赖,最适合入门级档案管理的可插拔扩展。

1.1 创建Spring Boot基础工程

直接使用官方Spring Initializr生成,操作路径:

1.2 定义档案管理的SPI接口

在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 listArchives(); } ```

再在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; } } ```

1.3 实现默认的本地存储扩展

在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 LOCAL_STORAGE = new ConcurrentHashMap<>(); @Override public String getStorageType() { return "local"; } @Override public boolean saveArchive(Archive archive) { LOCAL_STORAGE.put(archive.getId(), archive); return true; } @Override public Archive getArchiveById(String id) { return LOCAL_STORAGE.get(id); } @Override public List listArchives() { return new ArrayList<>(LOCAL_STORAGE.values()); } } ```

1.4 配置ServiceLoader与Spring Bean管理结合

在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 archiveStorageMap() { Map storageMap = new HashMap<>(); // 加载classpath下所有ArchiveStorage的实现类 ServiceLoader loader = ServiceLoader.load(ArchiveStorage.class); for (ArchiveStorage storage : loader) { storageMap.put(storage.getStorageType(), storage); } return storageMap; } } ```

在src/main/resources下新建META-INF/services目录,创建名为com.example.archivemanager.spi.ArchiveStorage的文件,内容:

``` com.example.archivemanager.spi.impl.LocalArchiveStorage ```

第二步:实现核心业务与扩展路由接口

2.1 创建路由用的Service

从零搭轻量可插拔档案管理软件:掌握易扩展核心三步

在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 archiveStorageMap; // 保存档案,默认用local存储,可传入指定type public boolean saveArchive(String storageType, String name, String content) { if (storageType == null || storageType.isEmpty()) { storageType = "local"; } ArchiveStorage storage = archiveStorageMap.get(storageType); if (storage == null) { throw new IllegalArgumentException("不支持的存储类型:" + storageType); } String id = UUID.randomUUID().toString(); return storage.saveArchive(new Archive(id, name, content)); } public Archive getArchiveById(String storageType, String id) { if (storageType == null || storageType.isEmpty()) { storageType = "local"; } ArchiveStorage storage = archiveStorageMap.get(storageType); if (storage == null) { throw new IllegalArgumentException("不支持的存储类型:" + storageType); } return storage.getArchiveById(id); } public List listArchives(String storageType) { if (storageType == null || storageType.isEmpty()) { storageType = "local"; } ArchiveStorage storage = archiveStorageMap.get(storageType); if (storage == null) { throw new IllegalArgumentException("不支持的存储类型:" + storageType); } return storage.listArchives(); } } ```

2.2 创建测试接口

在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 List list(@RequestParam(required = false) String storageType) { return archiveService.listArchives(storageType); } } ```

2.3 验证基础功能

  • 启动ArchiveManagerApplication类(IDEA右键主类Run)
  • 用Postman或curl测试保存:curl -X POST "http://localhost:8080/archive/save?name=入职档案&content=2024年入职",返回true
  • 测试查询所有:curl "http://localhost:8080/archive/list",返回刚才的入职档案

第三步:零代码修改添加新的MinIO存储扩展

这里的零代码修改指的是无需修改主工程的代码,只需要新建一个独立的Maven模块或者JAR包作为扩展。

3.1 创建独立的MinIO扩展模块

在IDEA主工程上右键→New→Module→Maven,Group填com.example,Artifact填archive-minio-storage,点击Create。

3.2 配置扩展模块的pom.xml

将主工程作为依赖引入,同时添加MinIO的依赖:

```xml 4.0.0 com.example archive-manager 0.0.1-SNAPSHOT archive-minio-storage com.example archive-manager 0.0.1-SNAPSHOT io.minio minio 8.5.7 org.springframework.boot spring-boot-configuration-processor true ```

3.3 实现MinIO存储类

在扩展模块的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 MINIO_META_MAP = new ConcurrentHashMap<>(); private final MinioClient minioClient; // 构造方法初始化MinIO连接(这里简化,参数可写死或用配置文件) public MinioArchiveStorage() { this.minioClient = MinioClient.builder() .endpoint("http://127.0.0.1:9000") .credentials("minioadmin", "minioadmin") .build(); // 自动创建bucket try { if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build())) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build()); } } catch (Exception e) { e.printStackTrace(); } } @Override public String getStorageType() { return "minio"; } @Override public boolean saveArchive(Archive archive) { try { // 保存元数据 MINIO_META_MAP.put(archive.getId(), archive); // 保存内容到MinIO InputStream stream = new ByteArrayInputStream(archive.getContent().getBytes(StandardCharsets.UTF_8)); minioClient.putObject( PutObjectArgs.builder() .bucket(BUCKET_NAME) .object(archive.getId()) .stream(stream, stream.available(), -1) .contentType("text/plain") .build() ); return true; } catch (Exception e) { e.printStackTrace(); return false; } } @Override public Archive getArchiveById(String id) { try { // 获取元数据 Archive archive = MINIO_META_MAP.get(id); if (archive == null) return null; // 获取MinIO内容覆盖原content try (InputStream stream = minioClient.getObject( GetObjectArgs.builder().bucket(BUCKET_NAME).object(id).build())) { String content = new String(stream.readAllBytes(), StandardCharsets.UTF_8); archive.setContent(content); } return archive; } catch (Exception e) { e.printStackTrace(); return null; } } @Override public List listArchives() { // 这里简化,实际可遍历MinIO bucket return new ArrayList<>(MINIO_META_MAP.values()); } } ```

3.4 配置扩展模块的SPI文件

在扩展模块的src/main/resources下新建META-INF/services目录,创建名为com.example.archivemanager.spi.ArchiveStorage的文件,内容:

``` com.example.archiveminio.MinioArchiveStorage ```

3.5 安装扩展并验证

  • 先安装Docker(如果没有):访问https://docs.docker.com/get-docker/ 下载对应系统版本并安装
  • 启动MinIO: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"
  • 在IDEA中先install主工程,再install扩展模块
  • 修改主工程的pom.xml,添加扩展模块依赖: ```xml com.example archive-minio-storage 0.0.1-SNAPSHOT ```
  • 重启主工程,测试MinIO存储:
    • 保存:curl -X POST "http://localhost:8080/archive/save?storageType=minio&name=离职档案&content=2025年离职",返回true
    • 查询单个:访问http://127.0.0.1:9001 登录minioadmin/minioadmin,查看archive-bucket下是否有对应ID的文件;用curl也能获取:curl "http://localhost:8080/archive/get?storageType=minio&id=刚才返回的UUID(可从list接口取)"
吃透档案库房管理规定与档案制度建设 告别档案管理杂乱踩坑
吃透档案库房管理规定与档案制度建设 告别档案管理杂乱踩坑
家人们谁懂啊,我前两年刚接单位档案管理的活的时候,那叫一个头大,档案室像个没人管的杂物间,扫把拖把、闲置的打印机、甚至年会剩的对联福字都往里面堆,调个十年前的项目档案得翻半小时,去年上级专项检查,翻到...
2026年06月30日 12:40:13
档案培训线上考试不方便怎么办
档案培训线上考试不方便怎么办
档案培训线上考试不方便,可通过排查操作原因、协调官方资源、申请合规调整等方式高效解决。以下将结合2026年档案行业最新考核要求,从具体可落地的维度展开详细解答,帮助考生轻松突破考试障碍。
2026年06月30日 12:40:13
微信咨询
电话联系
QQ客服
微信咨询一对一服务
服务热线: 028-8744 4417
QQ客服: 2305721818