当前位置: 首页 > 面试题库 >

覆盖Thread.sleep()

阮星火
2023-03-14
问题内容

我们很少有扩展基本类的类。我们注意到我们使用了“退出一些睡眠”方法,并且希望在发生睡眠时进行记录。有没有一种方法可以覆盖Thread.sleep方法,在其中我可以添加一些自定义逻辑(即记录),然后仅调用实际的Thread.sleep()?这样,我就不必更改在我的基类中使用Thread.sleep的所有位置。我也愿意接受其他选择。


问题答案:

您不能重写Thread.sleep方法,因为它是本机方法,所以无法对其进行分析或转换。一种方法是使用Java代理自动将日志记录添加到所有调用Thread.sleep的位置。

尽管下面的方法有效并且很有趣,但对于您而言,将所有对in的调用重构Thread.sleep为一个单独的方法并在其中添加日志记录可能会更好。

您可以在此处找到Java代理的简介。简而言之,它是一种特殊的机制,允许(以及其他)转换已加载的Java字节码。以下Java
Agent类示例通过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

下载JavaAssist并将其放入带有已编译Agent的jar所在的文件夹中。使用parameter运行您的应用程序-javaagent:loggerAgent.jar

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

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



 类似资料:
  • 我正在开发一个Spring集成/引导应用程序。我正在使用一个多文档(src/main/Resources/application.yml)来设置几个配置类的默认值(用@ConfigurationProperties注释)。pplicaiton.yml带有默认值,其中一些需要被覆盖,具体取决于环境。 我可以在目录中使用Java系统属性(-D...=...)、Spring“属性”(--...=...)

  • 问题内容: 我试图在php中覆盖我的位置,但是我仍然在php.ini文件中获得了2 mb的值。 问题答案: 通过进行设置时,这些设置不会有任何效果。 原因是PHP 在 执行脚本 之前 需要这些值。上载时,将在完成上载后执行目标脚本,因此PHP需要事先知道最大大小。 在,虚拟主机配置或文件中进行设置。一个典型的文件如下所示:

  • 问题内容: 我的Java应用程序引用了一个使用log4j日志记录的第三方jar文件。问题是该jar包含自己的log4j.properties文件,这会导致我的机器上的访问被拒绝的异常,但是我无法控制jar文件来更改其内容。 我尝试在应用程序的类路径中添加自己的log4j.properties文件,但似乎没有效果。如果我尝试使用PropertyConfigurator以编程方式导入自己的设置,则lo

  • 在上一章中,我们讨论了超类和子类。 如果一个类从其超类继承一个方法,那么只要它没有标记为final,就有可能覆盖该方法。 覆盖的好处是:能够定义特定于子类类型的行为,这意味着子类可以根据其需求实现父类方法。 在面向对象的术语中,覆盖意味着覆盖现有方法的功能。 例子 (Example) 我们来看一个例子。 class Animal { public void move() { Sy

  • 对于Spring Boot2.1,默认情况下禁用bean重写,这是一件好事。 然而,我确实有一些测试,其中我使用mockito用模拟实例替换bean。在默认设置下,使用这种配置的测试将由于bean重写而失败。 我发现唯一有效的方法是通过应用程序属性启用bean重写: 但是,我真的希望确保测试配置的bean定义设置最小,这一点将由spring在禁用重写的情况下指出。 我正在重写的bean是 在导入到

  • 问题内容: 有没有人有过重写JavaScript函数的经验? 哪些浏览器支持此功能? 哪些浏览器版本支持此功能? 覆盖该功能有哪些危险? 问题答案: 绝对是“受支持的”。这是您的网页,您可以使用它进行任何操作。 我已经这样做了,可以在不修改库的情况下而是通过潜入事件来跟踪分析事件。 使用代理模式: 如果需要,您也可以绕过对原始函数的调用(代理)