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

ByteBuddy Advice无法访问静态方法与字段的解决方法咨询

问题:ByteBuddy Advice增强FileInputStream构造方法时无法访问静态方法与字段

使用ByteBuddy的Advice对FileInputStream的单参String构造方法进行增强,onEnter方法可以被调用,但访问自定义静态方法JustPrint.sayHello()和静态字段iveBeenHere时抛出NoClassDefFoundError,代码及运行报错如下:

测试代码

package com.example.javaagent.instrumentation;

import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import static net.bytebuddy.matcher.ElementMatchers.*;

public class FileInputStreamConstructorInstrumentationTests2 {

    @Test
    public void testByteBuddyfileInputStreamConstructor() {
        ByteBuddyAgent.install();

        AgentBuilder.Identified.Extendable extendableAgentBuilder = new AgentBuilder.Default()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
                .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
                .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
                .ignore(none())
                .type(named("java.io.FileInputStream"))
                .transform(new AgentBuilder.Transformer.ForAdvice());

        extendableAgentBuilder = extendableAgentBuilder.transform(
                new AgentBuilder.Transformer.ForAdvice()
                        .include(Thread.currentThread().getContextClassLoader())
                        .advice(
                                isConstructor().and(takesArguments(1)).and(takesArgument(0, String.class)),
                                this.getClass().getName() + "$FileInputStreamCtorString"));

        extendableAgentBuilder.installOnByteBuddyAgent();

        String filename = "example.txt";
        createFile(filename);
        try {
            FileInputStream inputStream = new FileInputStream(filename);
            int data = inputStream.read();
            while (data != -1) {
                System.out.print((char) data);
                data = inputStream.read();
            }
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        deleteFile(filename);
    }

    public static class FileInputStreamCtorString {
        public static boolean iveBeenHere = false;

        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static void onEnter(@Advice.Argument(0) String name) {
            System.out.println("FileInputStreamCtorString: entered with param "+ name);
            System.out.println("FileInputStreamCtorString: second log line");
            try{
                JustPrint.sayHello();
            }catch (Throwable e){
                System.out.println("FileInputStreamCtorString: inside catch: " + e.getMessage());
                e.printStackTrace();
            }
            System.out.println("FileInputStreamCtorString: after calling static method");
            try {
                iveBeenHere = true;
            } catch (Throwable e) {
                System.out.println("FileInputStreamCtorString: inside catch: " + e.getMessage());
                e.printStackTrace();
            }
            System.out.println("FileInputStreamCtorString: after setting static field");
        }
    }

    public static class JustPrint{
        public static void sayHello(){
            System.out.println("Did I say 'hello'?");
        }
    }

    private static void deleteFile(String filename) {
        File file = new File(filename);
        if (file.exists()) {
            if (file.delete()) {
                System.out.println("File deleted successfully");
            } else {
                System.out.println("Failed to delete file");
            }
        } else {
            System.out.println("File does not exist");
        }
    }

    private static void createFile(String filename) {
        File file = new File(filename);
        if (!file.exists()) {
            try {
                file.createNewFile();
                System.out.println("File created successfully");
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("File already exists");
        }
    }
}

运行报错输出

[Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport@3af17be2 on sun.instrument.InstrumentationImpl@f9879ac
[Byte Buddy] REDEFINE BATCH #0 [1 of 1 type(s)]
[Byte Buddy] TRANSFORM java.io.FileInputStream [null, module java.base, Thread[Test worker,5,main], loaded=true]
[Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 1 types [0 failed batch(es)]
[Byte Buddy] INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport@3af17be2 on sun.instrument.InstrumentationImpl@f9879ac
File created successfully
FileInputStreamCtorString: entered with param example.txt
FileInputStreamCtorString: second log line
FileInputStreamCtorString: inside catchcom/example/javaagent/instrumentation/FileInputStreamConstructorInstrumentationTests2$JustPrint

java.lang.NoClassDefFoundError: com/example/javaagent/instrumentation/FileInputStreamConstructorInstrumentationTests2$JustPrint
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:111)
    at com.example.javaagent.instrumentation.FileInputStreamConstructorInstrumentationTests2.testByteBuddyfileInputStreamConstructor(FileInputStreamConstructorInstrumentationTests2.java:49)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    ....................
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

FileInputStreamCtorString: after calling static method
FileInputStreamCtorString: inside catchcom/example/javaagent/instrumentation/FileInputStreamConstructorInstrumentationTests2$FileInputStreamCtorString

java.lang.NoClassDefFoundError: com/example/javaagent/instrumentation/FileInputStreamConstructorInstrumentationTests2$FileInputStreamCtorString
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:111)
    at com.example.javaagent.instrumentation.FileInputStreamConstructorInstrumentationTests2.testByteBuddyfileInputStreamConstructor(FileInputStreamConstructorInstrumentationTests2.java:49)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    ...................
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

FileInputStreamCtorString: after setting static field
File deleted successfully

解决方案

问题根源

FileInputStream属于JDK核心模块java.base,Java 9+的模块系统默认限制模块间的访问权限。增强后的FileInputStream代码运行在java.base模块的上下文里,无法自动访问你自定义的类(位于未命名模块),导致类加载失败。

解决步骤

1. 添加JVM参数开放模块访问

在测试运行时添加以下JVM参数,允许java.base模块的java.io包访问未命名模块(你的测试类所在模块):

--add-opens java.base/java.io=ALL-UNNAMED

如果是Gradle项目,可以在build.gradle的测试配置中添加:

test {
    jvmArgs '--add-opens', 'java.base/java.io=ALL-UNNAMED'
}

2. 优化ByteBuddy配置(可选)

调整AgentBuilder的transform逻辑,合并重复的Transformer.ForAdvice配置,避免冗余:

AgentBuilder.Identified.Extendable extendableAgentBuilder = new AgentBuilder.Default()
        .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
        .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
        .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
        .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
        .ignore(none())
        .type(named("java.io.FileInputStream"))
        .transform(new AgentBuilder.Transformer.ForAdvice()
                .include(Thread.currentThread().getContextClassLoader())
                .advice(isConstructor().and(takesArguments(1)).and(takesArgument(0, String.class)),
                        FileInputStreamCtorString.class.getName()));

原理说明

添加--add-opens参数后,java.base模块的java.io包对未命名模块开放访问权限,增强后的FileInputStream代码就能正常加载并访问你自定义的静态类和字段。同时,确保ByteBuddy的include方法指定了包含自定义类的类加载器,保证Advice代码能正确找到依赖类。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:40:17