如何实现仅在依赖模块加载时运行的Java代码?
模块化3D模型处理库实现方案(基于SPI机制)
核心设计思路
采用Java原生的**服务提供者接口(SPI)**实现核心库与模型处理器模块的完全解耦:核心库仅定义统一接口,各3D模型类型的处理逻辑封装为独立模块(如dmal-dae),核心库通过SPI动态发现并加载可用的处理器,无需硬编码依赖关系。
分步实现
1. 核心库(dmal-core)定义基础接口与异常
首先在核心库中定义所有处理器必须实现的接口,以及自定义异常类:
统一处理器接口
// dmal-core 核心接口 public interface ModelProcessor { // 返回当前处理器支持的文件后缀(不带前缀点) String supportedExtension(); // 执行模型文件处理逻辑 void process(InputStream inputStream); }
自定义异常类
// dmal-core 自定义异常 public class ModelProcessorNotFoundException extends RuntimeException { public ModelProcessorNotFoundException(String extension) { super("未找到支持." + extension + "格式的处理器模块,请确认已添加对应依赖"); } }
2. 核心库实现动态加载与统一入口
Dmal类通过SPI自动加载所有可用的ModelProcessor实现,根据文件后缀匹配处理器:
// dmal-core 核心入口类 import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; public class Dmal { private final Map<String, ModelProcessor> processorCache; public Dmal() { processorCache = new HashMap<>(); // 通过SPI加载所有ModelProcessor实现并缓存 ServiceLoader<ModelProcessor> loader = ServiceLoader.load(ModelProcessor.class); for (ModelProcessor processor : loader) { processorCache.put(processor.supportedExtension().toLowerCase(), processor); } } public void process(String fileName) { String fileExtension = extractFileExtension(fileName).toLowerCase(); InputStream inputStream = loadFileToStream(fileName); ModelProcessor processor = processorCache.get(fileExtension); if (processor == null) { throw new ModelProcessorNotFoundException(fileExtension); } try { processor.process(inputStream); } finally { // 确保输入流关闭 try { inputStream.close(); } catch (Exception e) { e.printStackTrace(); } } } // 提取文件后缀 private String extractFileExtension(String fileName) { int lastDotIndex = fileName.lastIndexOf('.'); if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) { throw new IllegalArgumentException("无效文件名:" + fileName); } return fileName.substring(lastDotIndex + 1); } // 文件转输入流的实现(可根据实际场景调整,如本地文件、资源文件等) private InputStream loadFileToStream(String fileName) { try { return getClass().getClassLoader().getResourceAsStream(fileName); } catch (Exception e) { throw new RuntimeException("文件加载失败:" + fileName, e); } } }
3. 独立处理器模块实现(以dmal-dae为例)
在dmal-dae模块中实现ModelProcessor接口,并配置SPI元数据:
Collada处理器实现
// dmal-dae 模块中的处理器 import java.io.InputStream; public class ColladaProcessor implements ModelProcessor { @Override public String supportedExtension() { return "dae"; } @Override public void process(InputStream inputStream) { // 此处编写DAE文件的具体解析与处理逻辑 System.out.println("开始处理DAE模型文件..."); // 调用Collada解析库的代码写在这里 } }
SPI配置
在dmal-dae模块的resources/META-INF/services目录下创建文件,文件名是ModelProcessor接口的全类名(如com.example.dmal.core.ModelProcessor),文件内容为处理器类的全类名:
com.example.dmal.dae.ColladaProcessor
方案优势
- 完全解耦:核心库不依赖任何处理器模块,新增模型类型仅需添加独立模块,无需修改核心代码
- 轻量级:基于Java原生SPI机制,无需引入额外依赖
- 动态发现:处理器模块在运行时自动被加载,无需手动注册
- 异常清晰:依赖缺失时抛出明确的自定义异常,便于排查问题
内容的提问来源于stack exchange,提问作者Jordan Ellison
相关产品推荐
相关产品推荐

