Переопределить Thread.sleep()

#java #sleep #thread-sleep

#java #сон #поток-сон

Вопрос:

У нас есть несколько классов, которые расширяют базовый класс. Мы заметили, что используем метод выхода из нескольких спящих режимов, и мы хотели регистрировать, когда происходит спящий режим. Есть ли способ переопределить метод Thread.sleep, в котором я могу добавить некоторую пользовательскую логику (т.Е. Ведение журнала), а затем просто вызвать фактический Thread.sleep()? Таким образом, мне не пришлось бы менять все места, где Thread.sleep используется в моих базовых классах. Я открыт и для других вариантов.

Комментарии:

1. Thread.sleep() это статический метод, поэтому вы не можете его переопределить.

2. Была бы лучшая альтернатива для достижения того, что мне нужно, т.Е. Возможность входа в систему при возникновении сна без необходимости изменять весь мой код?

3. Вызовите свой собственный метод, который выполняет Thread.sleep() и ведение журнала. Нет причин переопределять его.

4. Короткий ответ, нет. Длинный ответ, может быть, с помощью AOP? Возможно, вам также придется использовать пользовательский загрузчик классов и обработку байтового кода java.lang.Thread .

5. Да, я пытался избежать необходимости рефакторинга всего кода для вызова этого нового пользовательского метода, переопределив его

Ответ №1:

Вы не можете переопределить Thread.sleep метод, вы также не можете его использовать или преобразовать, поскольку это собственный метод. Один из способов — автоматически добавлять ведение журнала во все места, которые вызывают Thread.sleep, используя агент Java.

Хотя следующий подход работает и довольно увлекателен, в вашем случае, вероятно, гораздо лучше преобразовать все вызовы 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()) amp;amp; "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 .

Вы можете загрузить полный пример. Просто извлеките его, откройте папку release и запустите приложение с java -cp loggerAgent.jar -javaagent:loggerAgent.jar Test

Больше информации и примеров можно найти в этой отличной статье.