您不能覆盖Thread.sleep 方法,也不能检测或转换它,因为它是本机方法。一种方法是使用 Java 代理自动将日志记录添加到所有调用 Thread.sleep 的位置。
虽然以下方法有效且非常有趣,但在您的情况下,将所有对 Thread.sleep 的调用重构为单独的方法并在那里添加日志记录可能会更好。
您可以找到introduction to Java Agents here。简而言之,它是一种特殊机制,它允许(除其他外)转换加载的 Java 字节码。下面的 Java 代理类示例使用 System.out 日志记录自动增强对 Thread.sleep 的所有调用,并测量在该方法中花费的时间:
package fi.schafer.agent;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class LoggingAgent {
public static void premain(String agentArgument, Instrumentation instrumentation) throws Exception {
instrumentation.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
return doClass(className, classBeingRedefined, classfileBuffer);
}
});
}
/**
* Method enhances calls to Thread.sleep with logging.
*/
private static byte[] doClass(String name, Class clazz, byte[] b) {
ClassPool pool = ClassPool.getDefault();
CtClass cl = null;
try {
cl = pool.makeClass(new java.io.ByteArrayInputStream(b));
final CtMethod[] targetMethods = cl.getDeclaredMethods();
for (CtMethod targetMethod : targetMethods) {
targetMethod.instrument(new ExprEditor() {
public void edit(final MethodCall m) throws CannotCompileException {
if ("java.lang.Thread".equals(m.getClassName()) && "sleep".equals(m.getMethodName())) {
m.replace("{long startMs = System.currentTimeMillis(); " +
"$_ = $proceed($$); " +
"long endMs = System.currentTimeMillis();" +
"System.out.println(\"Logging Thread.sleep call execution, ms: \" + (endMs-startMs));}");
}
}
});
return cl.toBytecode();
}
} catch (Exception e) {
System.err.println("Could not instrument " + name
+ ", exception : " + e.getMessage());
} finally {
if (cl != null) {
cl.detach();
}
}
return b;
}
}
您需要将其编译成 loggerAgent.jar 文件,并在其中包含以下 META-INF/MANIFEST.MF:
Manifest-Version: 1.0
Premain-Class: fi.schafer.agent.LoggingAgent
Boot-Class-Path: javassist.jar
Download JavaAssist 并将其与已编译代理的 jar 放在同一文件夹中。使用参数-javaagent:loggerAgent.jar 运行您的应用程序。
您可以download a full example。只需解压,打开文件夹 release 并使用 java -cp loggerAgent.jar -javaagent:loggerAgent.jar Test 运行应用程序
更多信息和更多示例可以在此excellent article 中找到。