☕ JavaTranslator 子系统
架构 Java 源码 stub 渲染JavaTranslator 把任意类(.class / .jar / .dex / .apk 内)渲染成可读的类 Java 源码 stub——字段、构造器、方法、泛型、注解一应俱全。它的秘密在于 MetaObject 抽象:由 MetaObjectFactory 按来源选择反射 / ASM / dexlib2 三条实现路径,并以
MetaObjectWithMapper装饰器接入 ProGuard 反混淆。
MetaObject:统一的类元模型
MetaObject 是抽象类,定义了一份「类应该有什么」的清单,三条实现各自用不同技术去填充同一份清单:
| 抽象方法 | 语义 |
|---|---|
getClassGenerics() / getSuperclassGenerics() | 类/父类泛型 |
getName() | 类名(含 $Inner 层级) |
getAnnotations() / getModifiers() | 注解与修饰符 |
getSuperclass() / getInterfaces() | 继承与实现 |
getDeclaredFields() / getDeclaredConstructors() / getDeclaredMethods() | 三桶声明 |
数据类随附在 MetaObject 内:InterfaceInfo / FieldInfo / ConstructorInfo / MethodInfo / AnnotationInfo / ParameterInfo / ExceptionInfo。
三路策略选择
MetaObjectFactory.buildMetaObject(className, archiveFile) 按来源扩展名分派,异常时逐级回退:
flowchart TD
B["buildMetaObject(className, archiveFile)"] --> E{"归档后缀?"}
E -- ".jar" --> J["反射:ClassUtils.loadClassFromJar"]
J --> JOK{"NoClassDefFoundError?"}
JOK -- 否 --> J1["MetaObjectClass(反射)"]
JOK -- 是 --> A["ASM 回退 → MetaObjectAsmClass"]
E -- ".class" --> A
A --> A1["ClassDetailsFiller + ClassReader"]
E -- ".dex" --> D["DexlibLoader.loadDexFile<br/>+ DexlibAdapter 找 ClassDef"]
D --> D1["MetaObjectDex"]
E -- ".apk" --> AP["MultidexReader.extractClassesDexWithClass<br/>定位含该类的 dex → dex 路径"]
AP --> D1
E -- ".aar" --> AR["提取内嵌 jar 到临时文件 → 走 jar 路径"]
AR --> J
E -- 其它/全失败" --> F["new MetaObjectClass(Exception.class)<br/>最坏兜底"]| 路径 | 触发来源 | 底层技术 | 实现类 |
|---|---|---|---|
| 反射 | .jar(class 可加载时) | ClassUtils.loadClassFromJar + Java 反射 | MetaObjectClass |
| ASM | .class、.jar 加载失败回退 | ClassDetailsFiller + ClassReader.accept 字节码解析 | MetaObjectAsmClass |
| dexlib2 | .dex、.apk 内类 | DexlibLoader.loadDexFile + DexlibAdapter | MetaObjectDex |
| 兜底 | 任意失败 | new MetaObjectClass(Exception.class) | MetaObjectClass |
💡 为什么优先反射? 反射拿到的
Class是运行时元数据,泛型(getGenericType)最完整;ASM 是字节码视角、也能取泛型但更绕;dexlib2 是纯静态解析。三路覆盖「JVM 外循环 / 不可加载 / 无 JVM」三类场景。
JavaTranslator 的渲染管线
apply() 按固定顺序把 MetaObject 的元数据拼成源码 stub:
public void apply() {
emitPackage(); // package + import(QualifiedTypesMap 收全名)
emitClassHeader(getName(), modifiers, superclass, interfaces, annotations);
emitFields(...); // 字段声明
emitConstructors(...); // 构造器
emitMethods(...); // 方法签名(含参数泛型)
fillImports(); fillClassDecl(); fillFields(); fillCtors(); fillMethods();
}依赖收集在翻译中同步进行:QualifiedTypesMap.namesMapper 记录每个用到的全限定类型,getDependencies() 返回 namesMapper.getFullTypes()——这就是「当前类依赖谁」的来源。
反混淆:MetaObjectWithMapper 装饰器
JavaTranslator.addMapper 用装饰器而不是继承来挂反混淆,保持 MetaObject 族不动:
public void addMapper(TokensMapper reverseMappings) {
this.metaObject = new MetaObjectWithMapper(this.metaObject, reverseMappings);
}MetaObjectWithMapper 包装原 metaObject + 反向映射表:getName() 查表命中即返回原始名,其余委托原对象。于是反混淆是逐名重写,类引用、继承链、方法声明处全部还原(详见 概念:proguard-mapping)。
与 ContentReader / TranslatorFactory 的衔接
- 来源 —
.class/.jar/.dex/.apk内任意类名由 ContentReader 列表提供,GUI 点中即createTranslator→ JavaTranslator。 - 回退 — TranslatorFactory 对一切未命中内置扩展名的元素落
new JavaTranslator(className, archiveFile),是全包默认翻译器。 - 产物 — 仍以
List<ELEMENT>输出,MODIFIER/IDENTIFIER/ANNOTATION等 TAG 让 DisplayArea 着色(见 Translator 核心)。
设计要点
- 🪞 一抽象三实现 — MetaObject 定义「长什么样」,反射/ASM/dexlib 各自回答「怎么拿到」,新增字节码后端零改动渲染层。
- 🛡️ 级联回退 — jar 加载失败降 ASM、全失败落 Exception 兜底,任何输入都能渲染出一个 stub。
- 🧈 装饰器反混淆 —
MetaObjectWithMapper透明包裹,原 MetaObject 族零污染。 - 🎯 默认翻译器 — JavaTranslator 既是分支之一,又是全包回退,实际覆盖绝大多数点击场景。