requires static 是 Java 9 模块系统中声明可选依赖的关键字,允许编译期声明、运行时按需加载厂商 SDK,结合服务提供者与静态类检查实现类型安全的多厂商适配。
requires static 是 java 9 引入的模块系统(jpms)中用于声明可选依赖的关键字,它允许模块在编译期声明对另一模块的依赖,但该依赖在运行时**非必需加载**——即当目标模块不存在时,不会导致 classnotfouderror 或模块解析失败,只要相关代码路径未实际执行即可。
这一机制非常适合云 SDK 的多厂商适配场景:你希望同一套基础监控/存储/日志逻辑能按需加载阿里云、华为 AGC、腾讯 QAPM 等不同 SDK,而无需为每个厂商单独打包或硬编码 if-else 判断类存在性。关键不在于“避免 ClassNotFoundException”,而在于**让初始化逻辑只在对应 SDK 可用时才触发,且整个模块结构保持静态可验证、无反射污染、类型安全**。
一、模块声明层:用 requires static 隔离厂商能力
在 module-info.java 中,不直接 requires com.tencent.qapm,而是:
module com.example.cloudcore {
// 核心能力(无厂商绑定)
exports com.example.cloudcore.storage;
exports com.example.cloudcore.log;
// 声明可选依赖:存在则可用,缺失也不报错
requires static com.tencent.qapm;
requires static com.huawei.agconnect;
requires static com.aliyun.popcore;
}
这样编译和启动时,即使工程里没引入 QAPM 的 JAR,模块也能正常解析;只有当你调用到明确依赖 QAPM 类型的代码时,JVM 才会尝试链接——而我们可以控制这个“调用时机”。
二、初始化入口:用服务提供者 + 静态检查兜底
定义统一初始化接口:
public interface CloudInitializer {
void init(Context context); // HarmonyOS Context / Android Context
boolean isAvailable(); // 运行时判断该 SDK 是否就绪
}
为每个厂商实现一个 独立的、延迟加载的初始化器(不放在主模块启动流程中):
-
TencentQAPMInitializer:内部仅引用QAPM、QAPMLoggerLevel等类型,且isAvailable()用Class.forName("com.tencent.qapm.QAPM") != null检查 -
HuaweiAGCStorageInitializer:只在方法体内 newAGCStorageClient(),并捕获NoClassDefFoundError -
AliyunROAInitializer:构造RPCClient前先确认Core.class可加载
⚠️ 注意:这些类本身不能被主模块直接 import,否则会导致模块解析失败。应通过 服务发现(如 ServiceLoader.load(CloudInitializer.class))或显式字符串类名反射加载(配合 try-catch),确保 JVM 不提前链接。
三、实战条件触发:结合配置与静态检查
真正初始化动作由业务方驱动,例如:
public class CloudAdapterManager {
private static final String CLOUD_VENDOR = System.getProperty("cloud.vendor", "huawei");
public static void tryInitStorage(Context ctx) {
switch (CLOUD_VENDOR) {
case "tencent":
new TencentQAPMInitializer().initIfAvailable(ctx);
break;
case "huawei":
new HuaweiAGCStorageInitializer().initIfAvailable(ctx);
break;
case "aliyun":
new AliyunROAInitializer().initIfAvailable(ctx);
break;
}
}
}
其中 initIfAvailable() 内部逻辑是:
- 先调用
isAvailable()—— 它只做Class.forName,不 new 实例 - 若返回 true,再执行真实初始化(此时 JVM 已确认类存在,不会抛 NoClassDefFoundError)
- 若 false,安静跳过,不打日志、不报警、不中断流程
这样既利用了 requires static 的编译期柔性,又通过运行时检查确保了行为确定性,比纯反射更可控、比多 module 分包更轻量。
四、HarmonyOS / Android 项目中的落地提示
在实际工程中要注意:
- AGC SDK 的
agconnect-services.json必须存在且含default_storage,否则initialize()会静默失败——requires static解决不了配置缺失问题 - 华为云存储初始化需传
Context,而Context是@ohos.app.ability.UIAbility类型,不能出现在纯 Java 模块中,建议将初始化逻辑下沉到 ArkTS 层或通过接口回调注入 - Android 上若用 Gradle Version Catalog,各厂商 SDK 应声明为
optional依赖,避免强制拉取
本质上,requires static 不是万能胶,而是帮你把“厂商耦合点”从模块依赖图里摘出来,把决策权交给运行时配置和轻量检查。真正解耦靠的是接口抽象 + 延迟加载 + 显式条件分支,不是语法糖。











