覆盖Thread.sleep()

use*_*708 4 java sleep thread-sleep

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

Vla*_*fer 5

您无法覆盖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;
    }

}
Run Code Online (Sandbox Code Playgroud)

您需要将其编译为loggerAgent.jar文件,并在其中包含以下META-INF/MANIFEST.MF:

Manifest-Version: 1.0
Premain-Class: fi.schafer.agent.LoggingAgent
Boot-Class-Path: javassist.jar
Run Code Online (Sandbox Code Playgroud)

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

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

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