You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java ASM ClassReader报找不到类异常,如何插桩OkHttpClient.Builder?

解决OkHttpClient.Builder插桩时ClassReader找不到类的问题及build()方法插桩实现

问题原因

你遇到的Class not found本质是类加载上下文不匹配:

  • 能正常实例化OkHttpClient.Builder是因为应用运行时类路径包含了OkHttp依赖,但插桩代码(比如Gradle构建阶段的Transform、运行时字节码修改工具)使用的类加载器,并没有将OkHttp的类纳入可搜索范围,直接用类名构造ClassReader自然找不到类。

解决方案

1. 正确获取类字节流(Gradle编译期插桩场景)

如果是在Gradle Transform中做字节码插桩,不要直接通过类名加载,而是遍历输入的Jar/目录文件,精准读取OkHttpClient$Builder.class的字节数组来创建ClassReader:

// 遍历Transform输入的Jar文件
for (JarInput jarInput : inputs) {
    try (JarFile jarFile = new JarFile(jarInput.getFile())) {
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            // 匹配Builder类的字节文件路径
            if ("okhttp3/OkHttpClient$Builder.class".equals(entry.getName())) {
                // 读取类字节流
                InputStream is = jarFile.getInputStream(entry);
                byte[] classBytes = new byte[(int) entry.getSize()];
                is.read(classBytes);
                // 创建ClassReader
                ClassReader cr = new ClassReader(classBytes);
                // 后续执行插桩逻辑
                processBuilderClass(cr);
                break;
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2. 用目标类的类加载器获取字节流(运行时插桩场景)

如果是在应用运行时做插桩,直接复用OkHttpClient.Builder自身的类加载器来读取字节流,避免当前类加载器的限制:

Class<?> builderClass = okhttp3.OkHttpClient.Builder.class;
// 通过类加载器获取类的字节文件输入流
InputStream is = builderClass.getClassLoader().getResourceAsStream("okhttp3/OkHttpClient$Builder.class");
if (is != null) {
    ClassReader cr = new ClassReader(is);
    // 执行插桩逻辑
}

3. 实现build()方法的拦截器插入逻辑

拿到ClassReader后,用ASM的ClassVisitor和MethodVisitor修改build()方法,在返回OkHttpClient实例前,插入添加自定义拦截器的逻辑:

private byte[] processBuilderClass(ClassReader cr) {
    ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
    ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
            // 匹配build()方法:方法名=build,方法描述符=()Lokhttp3/OkHttpClient;
            if ("build".equals(name) && "()Lokhttp3/OkHttpClient;".equals(desc)) {
                return new MethodVisitor(Opcodes.ASM9, mv) {
                    @Override
                    public void visitInsn(int opcode) {
                        // 在方法返回指令(ARETURN)前插入逻辑
                        if (opcode == Opcodes.ARETURN) {
                            // 加载当前Builder实例(this)
                            mv.visitVarInsn(Opcodes.ALOAD, 0);
                            // 创建自定义拦截器实例(替换为你的拦截器全类名)
                            mv.visitTypeInsn(Opcodes.NEW, "com/yourpackage/CustomOkHttpInterceptor");
                            mv.visitInsn(Opcodes.DUP);
                            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/yourpackage/CustomOkHttpInterceptor", "<init>", "()V", false);
                            // 调用Builder的addInterceptor方法
                            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "okhttp3/OkHttpClient$Builder", "addInterceptor", "(Lokhttp3/Interceptor;)Lokhttp3/OkHttpClient$Builder;", false);
                        }
                        super.visitInsn(opcode);
                    }
                };
            }
            return mv;
        }
    };
    cr.accept(cv, ClassReader.EXPAND_FRAMES);
    return cw.toByteArray();
}

注意事项

  • 确保ASM版本与目标Java/Android版本兼容(Android项目推荐ASM 9.x)。
  • Gradle插桩场景下,需将修改后的字节数组写回对应的输出文件(Jar或目录),避免插桩失效。
  • 配置混淆规则,保证OkHttpClient$Builder和自定义拦截器类不被混淆,否则插桩后的代码会因类名/方法名变更报错。

内容的提问来源于stack exchange,提问作者rysv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:52:43