覆盖Thread.sleep()

我们有几个扩展基类的类。 我们注意到我们使用退出一些睡眠方法,我们想在睡眠发生时记录。 有没有办法覆盖Thread.sleep方法,我可以在其中添加一些自定义逻辑(即日志记录),然后只调用实际的Thread.sleep()? 这样我就不必更改Thread.sleep在我的基类中使用的所有地方。 我也对其他选择持开放态度。

你不能覆盖Thread.sleep方法,你不能修改或转换它,因为它是一个本机方法。 一种方法是自动将日志记录添加到使用Java代理调用Thread.sleep的所有位置。

虽然以下方法有效且非常有趣,但在您的情况下,将所有对Thread.sleep调用重构为单独的方法并在其中添加日志记录可能要好得多。

您可以在此处找到Java代理的简介 。 简而言之,它是一种特殊的机制,允许(除此之外)转换加载的Java字节代码。 以下Java代理类示例自动增强对Thread.sleep的所有调用以及System.out日志记录并测量在该方法中花费的时间:

 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 

下载JavaAssist并将其放入与已编译代理的jar相同的文件夹中。 使用参数-javaagent:loggerAgent.jar运行应用程序。

您可以下载完整的示例 。 只需解压缩它,打开文件夹版本并使用java -cp loggerAgent.jar -javaagent:loggerAgent.jar Test运行应用程序java -cp loggerAgent.jar -javaagent:loggerAgent.jar Test

在这篇优秀的文章中可以找到更多信息和更多示例。