使用Java代理将类添加到类路径

Jon*_*her 3 java bytecode classpath javaagents javassist

我正在使用Java代理和Javassist为一些JDK类添加一些日志记录.本质上,当系统加载一些TLS类时,Javassist将为它们添加一些额外的字节码,以帮助我调试一些连接问题.

这是问题,因为这个类包含在代理jar中:

package com.something.myagent;
public class MyAgentPrinter {
    public static final void sayHello() {
        System.out.println("Hello!");
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的代理的转换方法中,假设我试图使用javassist调用该类:

// this is only called for sun.security.ssl.Handshaker
ClassPool cp = getClassPool(classfileBuffer, className);
CtClass cc = cp.get(className);
CtMethod declaredMethod = cc.getDeclaredMethod("calculateKeys");
declaredMethod.insertAfter("com.something.myagent.MyAgentPrinter.sayHello();");
cc.freeze();
return cc.toBytecode();
Run Code Online (Sandbox Code Playgroud)

你认为这会有效,但我得到了这个:

java.lang.NoClassDefFoundError: com/something/myagent/MyAgentPrinter
    at sun.security.ssl.Handshaker.printLogLine(Handshaker.java)
    at sun.security.ssl.Handshaker.calculateKeys(Handshaker.java:1160)
    at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:292)
Run Code Online (Sandbox Code Playgroud)

有没有办法将该类[ MyAgentPrinter] 添加到应用程序的类路径?

Hol*_*ger 5

你代理的jar文件,已添加到类路径中,由指定java.lang.instrument包文档:

代理类将由系统类加载器加载(请参阅参考资料ClassLoader.getSystemClassLoader).这是类加载器,它通常加载包含应用程序main方法的类.这些premain方法将在与应用程序main方法相同的安全性和类加载器规则下运行.

这就是为什么Javassist可以在转换字节代码时找到Agent的类.

问题似乎是您正在转换sun.**可能由引导加载程序或扩展加载程序加载的类.标准类加载委托是
application loader ? extension loader ? bootstrap loader,因此应用程序加载器可用的类不可用于扩展或引导加载程序加载的类.

因此,要使它们可用于所有类,您必须将代理程序的类添加到引导加载程序:

public class MyAgent {
    public static void premain(String agentArgs, Instrumentation inst) throws IOException {
        JarURLConnection connection = (JarURLConnection)
            MyAgent.class.getResource("MyAgent.class").openConnection();
        inst.appendToBootstrapClassLoaderSearch(connection.getJarFile());

        // proceed
    }
}
Run Code Online (Sandbox Code Playgroud)

在任何其他操作之前执行此操作至关重要,即在您希望为已检测的代码提供的类已加载之前.这意味着Agent类本身,即包含该premain方法的类不能被检测代码访问.Agent类也不应该直接引用MyAgentPrinter以避免意外的早期加载.

更可靠的方法是Boot-Class-Path在Agent jar的清单中添加一个条目,请参阅软件包文档的"Manifest Attributes"部分,以便在代理启动之前添加条目.但是,之后,jar文件的名称不得更改.