我正在尝试创建一个通用 Java 代理来检测任何 Java 应用程序的方法。我已经按照本教程https://javapapers.com/core-java/java-instrumentation/创建了一个 java 代理。java 代理应该寻找一个特定的类(我现在将它限制为一个类,因为它对我不起作用)一旦找到该类,我将使用 JavaAssist API 在每个类的开头添加一个局部变量方法并捕获当前时间。在方法的最后,我想简单地打印方法执行所花费的时间。(几乎遵循所有关于 Java 代理的典型示例。
我使用指向我创建的 Java 代理 jar 文件的 --javaagent 标志运行我的测试应用程序(使用 Vert.x 的 Web 服务器)(代码在下面)。
这适用于没有返回值和没有参数或返回/采用原始类型的方法。
但是,当一个方法返回或获取一个来自另一个类的对象(我认为尚未加载)的参数时,我会收到一个 CannotCompileException 异常,并显示该类在参数列表或返回语句中没有找到。例如,此方法的仪器有效:
@Override
public void start() throws Exception {
logger.debug("started thread {}", Thread.currentThread().getName());
for (int port : ports) {
HttpServer httpServer = getVertx().createHttpServer(httpServerOptions);
Router router = setupRoutes();
httpServer.requestHandler(router::accept);
logger.info("Listening on port {}", port);
httpServer.listen(port);
}
}
但是对于返回 io.vertx.ext.web.Router 的这个方法:
private Router setupRoutes() {
Router router = Router.router(getVertx());
router.get(STATUS_PATH).handler(this::statusHandler);
router.route().handler(BodyHandler.create());
router.post().handler(this::handleBidRequest);
router.put().handler(this::handleBidRequest);
router.get(SLEEP_CONTROLLER_PATH).handler(this::sleepControllerHandler);
return router;
}
我得到一个异常,我的 java 代理的输出是:
Instrumenting method rubiconproject.com.WebServerVerticle.setupRoutes()
Could not instrument method setupRoutes error: cannot find io.vertx.ext.web.Router
这是我的 java 代理的代码:
import java.lang.instrument.Instrumentation;
import transformers.TimeMeasuringTransformer;
public class TimeCapturerAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println(TimeCapturerAgent.class.getCanonicalName() + " is loaded...... ");
inst.addTransformer(new TimeMeasuringTransformer());
}}
package transformers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class TimeMeasuringTransformer implements ClassFileTransformer {
public TimeMeasuringTransformer() {
System.out.println("TimeMeasuringTransformer added ");
}
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if(className != null && className.contains("WebServerVerticle")) {
System.out.println("Instrumenting class " + className);
return modifyClass(classfileBuffer);
}
return null;
}
private byte[] modifyClass(byte[] originalClassfileBuffer) {
ClassPool classPool = ClassPool.getDefault();
CtClass compiledClass;
try {
compiledClass = classPool.makeClass(new ByteArrayInputStream(originalClassfileBuffer));
System.out.println("Created new compiled Class " + compiledClass.getName());
} catch (IOException e) {
e.printStackTrace();
return null;
}
instrumentMethods(compiledClass);
byte [] newClassByteCode = createNewClassByteArray(compiledClass);
compiledClass.detach();
return newClassByteCode;
}
private byte[] createNewClassByteArray(CtClass compiledClass) {
byte[] newClassByteArray = null;
try {
newClassByteArray = compiledClass.toBytecode();
} catch (IOException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} finally {
return newClassByteArray;
}
}
private void instrumentMethods(CtClass compiledClass) {
CtMethod[] methods = compiledClass.getDeclaredMethods();
System.out.println("Class has " + methods.length + " methods");
for (CtMethod method : methods) {
try {
System.out.println("Instrumenting method " + method.getLongName());
method.addLocalVariable("startTime", CtClass.longType);
method.insertBefore("startTime = System.nanoTime();");
method.insertAfter("System.out.println(\"Execution Duration "
+ "(nano sec): \"+ (System.nanoTime() - startTime) );");
} catch (CannotCompileException e) {
System.out.println("Could not instrument method " + method.getName()+" error: " + e.getMessage());
continue;
}
}
}}