Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 加载某些类时不会调用ClassFileTransformer.transform()_Java_Instrumentation - Fatal编程技术网

Java 加载某些类时不会调用ClassFileTransformer.transform()

Java 加载某些类时不会调用ClassFileTransformer.transform(),java,instrumentation,Java,Instrumentation,我试图用Javaflow()和OW2 ASM库()为java类提供工具。我设置了javaagent,并使用Instrumentation.addTransformer()在加载时将我的转换器注册到instrument类。在转换器内部,我检查是否需要检测类。如果是,则执行检测 症状: 总之:在加载和检测某些类之后,不会在类加载时调用transform() 详情如下: 一切都很顺利。正确调用premain,并且在加载的前几个类上调用我的transformer。在一个特定的类(比如Foo)被检测后,突

我试图用Javaflow()和OW2 ASM库()为java类提供工具。我设置了javaagent,并使用Instrumentation.addTransformer()在加载时将我的转换器注册到instrument类。在转换器内部,我检查是否需要检测类。如果是,则执行检测

症状: 总之:在加载和检测某些类之后,不会在类加载时调用transform()

详情如下: 一切都很顺利。正确调用premain,并且在加载的前几个类上调用我的transformer。在一个特定的类(比如Foo)被检测后,突然间事情开始变得非常糟糕。我可以看到正在加载的类(通过-verbose:class消息),但在这些类上并没有调用transform()方法。这些类是可修改的。如果我选择不插入类Foo,则所有其他类都会正确加载和插入

可能有助于诊断问题的更多信息: 1.我在想,在检测类Foo时,transform()内部可能会引发一些异常。然后我将transform()中的代码封装在try-catch语句中。然而,没有发现任何例外情况。 2.似乎不仅仅是Foo类可以触发这个问题。其他一些类(我不需要对其进行检测,但为了测试目的对其进行检测)也可能触发此问题。我找不到可能引发这个问题的类之间有多少相似之处。 3.如果在transform()内部,我不执行任何插装,则transform()会正确地钩住所有类加载

有人知道是什么导致了这个问题吗?如果有必要,我可以提供更多关于我正在做什么的信息

更新:显示症状的最低示例: 目录结构如下所示:

src\
    instrumentation\
        Instrumentor.java
    test\
        InstrumentationTest.java
        TestRunnable.java
lib\
    asm-4.0.jar
    asm-commons-4.0.jar
    asm-util-4.0.jar
    commons-logging-1.1.3.jar
    asm-analysis-4.0.jar
    asm-tree-4.0.jar
    commons-javaflow-2.0-SNAPSHOT.jar
以下是Instrumentor.java的内容:

package instrumentation;

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

import org.apache.commons.javaflow.bytecode.transformation.ResourceTransformer;
import org.apache.commons.javaflow.bytecode.transformation.asm.AsmClassTransformer;

public class Instrumentor implements ClassFileTransformer {
    public static void premain(String agentArguments,
        Instrumentation instrumentation) {
        try {
            System.out.println("Executing premain");
            instrumentation.addTransformer(new Instrumentor());
        } catch (Exception e) {
            System.out.println("Exception.");
            e.printStackTrace();
        }
    }

    static String[] toInstrument = new String[] { "test/InstrumentationTest", "test/TestRunnable" };

    // static String[] toInstrument = new String[] { "test/TestRunnable" };

    @Override
    public byte[] transform(ClassLoader loader, String className,
        Class redefiningClass, ProtectionDomain domain, byte[] bytes)
        throws IllegalClassFormatException {
        try {
            ResourceTransformer continuationTransformer = new AsmClassTransformer();
            if (className.equals("test/TestRunnable") || className.equals("test/InstrumentationTest")) {
                System.out.println(className + " intercepted.");
            }
            for (String ti : toInstrument) {
                if (className.equals(ti)) {
                    System.out.println("Instrumenting " + className);
                    byte[] continuationClass = continuationTransformer.transform(bytes);
                    return continuationClass;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // System.out.println("Not instrumented. " + className);
        return bytes;
    }
}
然后是InstrumentationTest.java的内容:

package test;
public class InstrumentationTest {
    public static void main(String args[]) {
        Runnable r = new TestRunnable();
        r.run();
    }
}
最后是TestRunnable.java的内容:

package test;
public class TestRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++)
            System.out.println(i);
    }
}
封装测试;
公共类TestRunnable实现Runnable{
@凌驾
公开募捐{
对于(int i=0;i<5;i++)
系统输出打印LN(i);
}
}
下面是一个示例生成文件:

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="package" basedir=".">
  <fileset id="lib" dir="lib" includes="**/*.jar"/>
  <pathconvert property="libs" refid="lib" pathsep=":" />
  <target name="compile" description="compile class files.">
    <mkdir dir="build/classes" />
    <javac srcdir="src" destdir="build/classes" debug="true" debuglevel="lines,vars,source">
      <classpath>
        <fileset dir="lib" includes="**/*.jar" />
      </classpath>
    </javac>
  </target>
  <target name="package" description="build the jar file from the compiled classes." depends="compile">
    <mkdir dir="build" />
    <jar jarfile="build/agent.jar" basedir="build/classes">
      <manifest>
        <attribute name="Premain-Class" value="instrumentation.Instrumentor" />
      </manifest>
    </jar>
  </target>
  <target name="run" description="run the test" depends="package" >
    <java fork="true" classname="test.InstrumentationTest">
      <classpath>
        <fileset dir="lib" includes="**/*.jar" />
        <pathelement path="./build/classes" />
      </classpath>
      <jvmarg value="-Xbootclasspath/p:${libs}" />
      <jvmarg value="-javaagent:./build/agent.jar" />
    </java>
  </target>
  <target name="clean">
    <delete dir="build" />
  </target>
</project>


然后,如果您运行“ant run”,您会发现只有InstrumentationTest被检测,而TestRunnable没有(它的负载甚至没有被transform()截获)。您可以切换toInstrument数组上的注释,以查看如果InstrumentationTest未插入指令,transform()会正确拦截所有类的负载,并插入TestRunnable。

您是否注意到在“Instrumentation.addTransformer”方法的javadoc中,它说:“所有未来的类定义都将由transformer查看,除了任何注册的transformer依赖的类的定义之外。”


你的案子有这种情况吗

我想你应该看看这个线程中被接受的答案:如果没有一个答案适用,请提供一个示例来说明这个问题。@mttdbrd我确实看过这个线程。我没有使用重定义,我想我已经正确设置了Xbootclasspath和Pre-main。我也尝试捕捉所有异常。但仍然没有发现异常。我想知道这是否是因为在调用premain之前,类加载器已经加载了线程。既然你没有把它当作线程来调用,那就试着从
TestRunnable
@mttdbrd中删除
extends-thread
。哦,它必须是一个线程。为了简单起见,我删除了将其用作代码中线程的部分。但是是的,线程在premain之前加载。我的问题是为什么(你认为)这可能很重要?如果它需要是线程,它没有被用作线程。您需要调用start()而不是run()。另外,我要实现Runnable而不是扩展线程。Java不支持多重继承,但它允许您实现多个接口。我认为这可能是一个问题的原因是,像Thread这样的系统类将在调用premain之前加载。