Java ClassFileTransformer 失败抛出异常

问题描述 投票:0回答:1

我有一些代码尝试在运行时使用

ClassFileTransformer
Instrumentation
的实例重新定义类。 但是,我注意到
transform
ClassFileTransformer
方法无法抛出任何异常。

这是一个示例(在 java 8 和 17 中测试):

import net.bytebuddy.agent.ByteBuddyAgent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class Main {
    public static void main(String[] args) {

        // implementation 'net.bytebuddy:byte-buddy-agent:1.14.13'
        ByteBuddyAgent.install();
        Instrumentation instrumentation = ByteBuddyAgent.getInstrumentation();

        instrumentation.addTransformer(new Transformer(), true);

        try {
            instrumentation.retransformClasses(Klass.class);
        }catch (Exception e) {
            System.out.println("An error occurred: " + e.getMessage());
            return;
        }

        System.out.println("Finished successfully");

    }

    private static class Transformer implements ClassFileTransformer {

        @Override
        public byte[] transform(ClassLoader cl, String name, Class<?> klass, ProtectionDomain pd, byte[] classfileBuffer) {
            System.out.println("Transforming class " + klass.getName());
            throw new RuntimeException("Example exception");
        }
    }

    private static class Klass {}

}

此代码抛出异常,并且逻辑上应该触发“发生错误:”消息。

相反,catch 块没有捕获任何异常:

Transforming class Main$Klass
Finished successfully

您知道是什么原因导致这种行为以及是否可以避免吗?

java instrumentation java-bytecode-asm byte-buddy
1个回答
0
投票

用户Slaw评论时是对的:

来自

ClassFileTransformer
:“如果变压器抛出异常(它没有捕获),后续变压器仍将被调用,并且仍将尝试加载、重新定义或重新转换。因此,抛出异常具有相同的效果返回 null 的效果。”

此提示归功于他/她。不过,我想添加一些细节:

只需打印堆栈跟踪(或对其进行调试)即可查看如何调用您的

transform
方法:

  private static class Transformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader cl, String name, Class<?> klass, ProtectionDomain pd, byte[] classfileBuffer) {
      System.out.println("Transforming class " + klass.getName());
      new RuntimeException("Example exception").printStackTrace(System.out);
      throw new RuntimeException("Example exception");
    }
  }

JDK 21 的控制台日志:

Transforming class Main$Klass
java.lang.RuntimeException: Example exception
    at Main$Transformer.transform(Main.java:27)
    at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:244)
    at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:610)
    at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:225)
    at Main.main(Main.java:14)
Finished successfully

现在,让我们看一下 JDK 类

TransformerManager
,找到代码中故意吞掉异常的位置(空白稍微重新格式化):


try {
  transformedBytes = transformer.transform(module, loader, classname, classBeingRedefined, protectionDomain, bufferToUse);
}
catch (Throwable t) {
  // don't let any one transformer mess it up for the others.
  // This is where we need to put some logging. What should go here? FIXME
}
© www.soinside.com 2019 - 2024. All rights reserved.