PluginLoader
5 个集成的反射加载
基本信息
| 属性 | 值 |
|---|---|
| 路径 | com.github.lunatrius.ingameinfo.integration.PluginLoader |
| 行数 | 70 |
| 单例 | 懒加载 getInstance()(:17-23) |
5 个集成的类名是字符串硬编码(:26-30)
final String[] names = new String[] {
"com.github.lunatrius.ingameinfo.integration.bloodmagic.BloodMagic",
"com.github.lunatrius.ingameinfo.integration.simplyjetpacks.SimplyJetpacks",
"com.github.lunatrius.ingameinfo.integration.terrafirmacraft.TerraFirmaCraft",
"com.github.lunatrius.ingameinfo.integration.thaumcraft.Thaumcraft",
"com.github.lunatrius.ingameinfo.integration.gregtech.Gregtech" };
⚠️ 数组顺序与包名字母序不一致:
bloodmagic → simplyjetpacks → terrafirmacraft → thaumcraft → gregtech
(gregtech 在最后,不在 g 位置)。
⚠️ 用字符串反射而非 Class.forName on 直接引用,
是为了让未安装的集成不触发类加载。若直接 import + new,
NoClassDefFoundError 会在 preInit 直接崩掉整个 mod。
⚠️ 代价:改名/移包不会编译期报错,只在运行期记
"{} not found, skipping..."。
preInit(:25-58)—— 反射加载 + 筛选
for (final String name : names) {
try {
final Class<?> clazz = Class.forName(name);
if (!Plugin.class.isAssignableFrom(clazz)) { continue; } // :36-38
@SuppressWarnings("unchecked")
final Class<? extends Plugin> clazzPlugin = (Class<? extends Plugin>) clazz;
final Plugin plugin = clazzPlugin.newInstance(); // :42
if (plugin.canLoad()) { this.plugins.add(plugin); } // :43-45
} catch (ClassNotFoundException cnfe) { logger.error("{} not found, skipping...", name); }
catch (InstantiationException ie) { logger.error("{} could not be created, skipping...", name); }
catch (IllegalAccessException iae) { logger.error("{} could not be accessed, skipping...", name); }
}
if (this.plugins.size() > 0) {
event.getModMetadata().description += "\nCompiled against " + this.plugins; // :56
}
| 步骤 | 说明 |
|---|---|
Class.forName |
触发类加载;未安装的集成类仍在本 jar 内,所以这里不会抛 ClassNotFoundException |
isAssignableFrom |
类型检查,不匹配则静默 continue(不记日志) |
newInstance() |
已废弃 API(Java 9+),等价 getDeclaredConstructor().newInstance() |
canLoad() |
Loader.isModLoaded(依赖 modid) |
⚠️ Class.forName 在这里几乎不会抛 ClassNotFoundException ——
5 个集成类都编译进本 mod 的 jar(它们是本 mod 的源码),
Class.forName 一定能找到。真正决定「集成是否生效」的是
canLoad()(目标 mod 是否安装)。
所以 catch (ClassNotFoundException) 是防御性代码,
只在 jar 损坏/类被裁剪时才触发。
⚠️ newInstance() 要求类有 public 无参构造器。
5 个插件类都满足(隐式 public 无参构造器)。
⚠️ isAssignableFrom 失败时静默 continue(:36-38),
不记日志 —— 若有人写了不继承 Plugin 的类,会无声消失。
追加到元数据(:55-57)
只在 plugins.size() > 0 时追加。
⚠️ 该 += 无幂等保护,重复调 preInit 会重复追加
(FML 保证只调一次,实际不会)。
postInit(:60-69)—— 真正 load + 释放单例
for (final Plugin plugin : this.plugins) {
plugin.load();
Reference.logger.debug("Loaded {} integration.", plugin.getDependencyName());
}
instance = null; // :68 "all plugins loaded, let it go~"
| 步骤 | 说明 |
|---|---|
plugin.load() |
各插件调自己的 TagXxx.register(),把集成 tag 加进 TagRegistry |
logger.debug |
debug 级别,默认不可见 |
instance = null |
把单例置空,下次 getInstance() 会重建 |
⚠️ instance = null 是有意为之(源码注释 :67
“all plugins loaded, let it go~”),
目的是让 PluginLoader 可被 GC 回收。
⚠️ 但 ClientProxy 在 postInit 里
用 PluginLoader.getInstance().postInit(event)(:62)调用 ——
调用返回后实例已为 null。若之后再调 getInstance(),
会得到一个空的 PluginLoader(plugins 列表为空)。
postInit 之后不再有调用点,所以无实际问题。
服务端不执行 postInit
ServerProxy 未覆写 postInit
(继承 CommonProxy 的空实现),
所以服务端的 PluginLoader.instance 永不被置 null。
详见 ServerProxy 条目。
调用时机(由 ClientProxy 决定)
| 阶段 | 调用 | 位置 |
|---|---|---|
preInit |
PluginLoader.getInstance().preInit(event) |
ClientProxy :37 |
postInit |
PluginLoader.getInstance().postInit(event) |
同上 :62 |
⚠️ preInit 在 core.reloadConfig()(:43)之前,
但 postInit 在它之后 ——
所以集成 tag 的注册晚于配置解析,
首次启动时配置里的集成 tag 会解析成 <ERROR>。
详见 ClientProxy 条目。
相关
- Plugin - 被加载的基类
- TagRegistry - 集成 tag 的注册目标
- ClientProxy - 两阶段调用方
- TagIntegration - 集成 tag 的异常处理基类