我正在使用Java Agent和Javassist向一些JDK类添加一些日志记录。本质上,当系统加载某些TLS类时,Javassist将向它们添加一些其他字节码,以帮助我调试一些连接问题。
考虑到此类包含在代理jar中,这就是问题所在:
package com.something.myagent;
public class MyAgentPrinter {
public static final void sayHello() {
System.out.println("Hello!");
}
}
在我的代理的transform方法中,假设我尝试使用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();
您认为这可行,但是我得到了:
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)
有什么方法可以将该类[ MyAgentPrinter
] 添加到应用程序的类路径中?
你代理的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
}
}
在执行任何其他操作之前,即在加载要提供给检测代码的类之前,请务必执行此操作。这意味着Agent类本身(即包含该premain
方法的类)无法被检测代码访问。Agent类也不应直接引用MyAgentPrinter
以避免意外的早期加载。
一种更可靠的方法是将一个Boot-Class-Path
条目添加到Agent jar的清单中,请参见软件包文档的“清单属性”部分,以便在Agent启动之前添加该条目。但是,之后jar文件的名称一定不能更改。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句