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

使用Java代理将类添加到类路径,java,classpath,bytecode,javassist,javaagents,Java,Classpath,Bytecode,Javassist,Javaagents,我正在使用Java代理和Javassist向一些JDK类添加一些日志记录。本质上,当系统加载一些TLS类时,Javassist将向它们添加一些额外的字节码,以帮助我调试一些连接问题 问题是,如果这个类包含在代理jar中: package com.something.myagent; public class MyAgentPrinter { public static final void sayHello() { System.out.println("Hello!")

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

问题是,如果这个类包含在代理jar中:

package com.something.myagent;
public class MyAgentPrinter {
    public static final void sayHello() {
        System.out.println("Hello!");
    }
}
在我的代理的转换方法中,假设我尝试使用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文件已添加到类路径,具体如下所示:

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

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

问题似乎是您正在转换一个可能由引导加载程序或扩展加载程序加载的
sun.*
类。标准类加载委托是
应用程序加载器→ 扩展装载机→ 引导加载程序
,因此应用程序加载程序可用的类对扩展或引导加载程序加载的类不可用

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

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
    }
}
在执行任何其他操作之前,即在加载要使插入指令的代码可用的类之前,执行此操作非常重要。这意味着代理类本身,即包含
premain
方法的类不能被插入指令的代码访问。代理类也不应该直接引用MyAgentPrint,以避免意外的早期加载


更可靠的方法是将
引导类路径
条目添加到代理jar的清单中,请参阅,以便在代理启动之前添加该条目。但是,jar文件的名称之后不得更改。

您说
代理类将由系统类加载器加载(请参阅ClassLoader.getSystemClassLoader)。
但是问题是找不到类
AgentPrinter
。如果
agentprint
MyAgent
位于同一目录中,则您是对的,否则可能是我said@rakwaht:我的答案解释了为什么Javassist没有失败,即找到类,而插入指令的代码却失败。看起来你是对的。我删除了我的答案,因为它是错的。附言:我投了赞成票:)绝对是很棒的答案。非常感谢。