【问题标题】:Override Thread.sleep()覆盖 Thread.sleep()
【发布时间】:2014-08-18 03:36:35
【问题描述】:

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

【问题讨论】:

  • Thread.sleep() 是一个静态方法,所以你不能覆盖它。
  • 是否有更好的替代方案来实现我所需要的,即能够在睡眠发生时进行记录,而无需更改我的所有代码?
  • 调用您自己的方法来执行Thread.sleep() 和日志记录。没有理由覆盖它。
  • 简答,不。长答案,也许是 AOP?您可能还必须在 java.lang.Thread 上使用自定义类加载器和字节码操作。
  • 是的,我试图通过重写来避免重构所有代码来调用这个新的自定义方法

标签: java sleep thread-sleep


【解决方案1】:

您不能覆盖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 中找到。

【讨论】:

    猜你喜欢
    • 2011-06-11
    • 2012-01-28
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 2021-06-26
    • 2020-06-06
    • 2011-01-23
    • 2016-12-10
    相关资源
    最近更新 更多