development

현재 실행중인 메소드의 이름 얻기

big-blog 2020. 2. 17. 22:21
반응형

현재 실행중인 메소드의 이름 얻기


Java로 현재 실행중인 메소드의 이름을 얻는 방법이 있습니까?


Thread.currentThread().getStackTrace()일반적으로 호출하는 메소드가 포함되지만 함정이 있습니다 ( Javadoc 참조 ).

일부 가상 머신은 경우에 따라 스택 추적에서 하나 이상의 스택 프레임을 생략 할 수 있습니다. 극단적 인 경우,이 스레드에 관한 스택 추적 정보가없는 가상 머신은이 메소드에서 길이가 0 인 배열을 리턴 할 수 있습니다.


기술적으로 이것은 작동합니다 ...

String name = new Object(){}.getClass().getEnclosingMethod().getName();

그러나 컴파일 시간 동안 새로운 익명의 내부 클래스가 만들어집니다 (예 :) YourClass$1.class. 따라서이 .class트릭을 배포하는 각 방법에 대한 파일 이 생성됩니다 . 또한 런타임 동안 호출 할 때마다 사용하지 않는 객체 인스턴스가 생성됩니다. 따라서 이것은 허용 가능한 디버그 트릭 일 수 있지만 상당한 오버 헤드가 있습니다.

이 트릭의 장점은 주석 및 매개 변수 이름을 포함하여 메소드의 다른 모든 정보를 검색하는 데 사용할 수있는 getEncosingMethod()리턴 java.lang.reflect.Method입니다. 이를 통해 동일한 이름을 가진 특정 메소드 (방법 과부하)를 구별 할 수 있습니다.

getEnclosingMethod()이 속임수 의 JavaDoc에 따르면 SecurityException동일한 클래스 로더를 사용하여 내부 클래스를로드해야하므로 as를 던져서는 안됩니다. 따라서 보안 관리자가 있어도 액세스 조건을 확인할 필요가 없습니다.

getEnclosingConstructor()생성자 에 사용해야 합니다. (명명 된) 메소드 외부의 블록 중에는을 getEnclosingMethod()리턴합니다 null.


2009 년 1 월 : @Bombe의주의 사항 을 염두에두고
전체 코드를 작성합니다 .

/**
 * Get the method name for a depth in call stack. <br />
 * Utility function
 * @param depth depth in the call stack (0 means current method, 1 means call method, ...)
 * @return method name
 */
public static String getMethodName(final int depth)
{
  final StackTraceElement[] ste = Thread.currentThread().getStackTrace();

  //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
  // return ste[ste.length - depth].getMethodName();  //Wrong, fails for depth = 0
  return ste[ste.length - 1 - depth].getMethodName(); //Thank you Tom Tresansky
}

이 질문에 더 많은 .

2011 년 12 월 업데이트 :

푸른 의견 :

JRE 6을 사용하고 잘못된 메소드 이름을 제공합니다.
내가 쓰면 작동ste[2 + depth].getMethodName().

  • 0입니다 getStackTrace(),
  • 1이다 getMethodName(int depth)
  • 2 메소드를 호출합니다.

virgo47답변 (공개)은 실제로 메소드 이름을 다시 얻기 위해 적용 할 올바른 색인을 계산합니다.


이 코드를 사용하여 스택 추적 인덱스의 잠재적 변동성을 완화했습니다. 이제 methodName util을 호출하면됩니다.

public class MethodNameTest {
    private static final int CLIENT_CODE_STACK_INDEX;

    static {
        // Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
        int i = 0;
        for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
            i++;
            if (ste.getClassName().equals(MethodNameTest.class.getName())) {
                break;
            }
        }
        CLIENT_CODE_STACK_INDEX = i;
    }

    public static void main(String[] args) {
        System.out.println("methodName() = " + methodName());
        System.out.println("CLIENT_CODE_STACK_INDEX = " + CLIENT_CODE_STACK_INDEX);
    }

    public static String methodName() {
        return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX].getMethodName();
    }
}

오버 엔지니어링 된 것 같지만 JDK 1.5에 대한 고정 번호가 있었고 JDK 1.6으로 옮길 때 약간 바뀌 었다는 것에 약간 놀랐습니다. 이제는 Java 6/7에서 동일하지만 결코 알 수 없습니다. 런타임 동안 해당 인덱스의 변경에 대한 증거는 아니지만 핫스팟이 그렇게 나쁘게하지 않기를 바랍니다. :-)


 public class SomeClass {
   public void foo(){
      class Local {};
      String name = Local.class.getEnclosingMethod().getName();
   }
 }

name의 값은 foo입니다.


내가 찾은 가장 빠른 방법 은 다음과 같습니다.

import java.lang.reflect.Method;

public class TraceHelper {
    // save it static to have it available on every call
    private static Method m;

    static {
        try {
            m = Throwable.class.getDeclaredMethod("getStackTraceElement",
                    int.class);
            m.setAccessible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getMethodName(final int depth) {
        try {
            StackTraceElement element = (StackTraceElement) m.invoke(
                    new Throwable(), depth + 1);
            return element.getMethodName();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

원시 메소드 getStackTraceElement (int depth)에 직접 액세스합니다. 그리고 접근 가능한 메소드를 정적 변수에 저장합니다.


이 두 가지 옵션 모두 Java에서 작동합니다.

new Object(){}.getClass().getEnclosingMethod().getName()

또는:

Thread.currentThread().getStackTrace()[1].getMethodName()

다음 코드를 사용하십시오.

    StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
    StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
    String methodName = e.getMethodName();
    System.out.println(methodName);

public static String getCurrentMethodName() {
        return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
    }

이것은 virgo47의 답변 (위) 에 대한 확장입니다 .

현재 및 호출 클래스 / 메소드 이름을 가져 오기위한 정적 메소드를 제공합니다.

/* Utility class: Getting the name of the current executing method 
 * https://stackoverflow.com/questions/442747/getting-the-name-of-the-current-executing-method
 * 
 * Provides: 
 * 
 *      getCurrentClassName()
 *      getCurrentMethodName()
 *      getCurrentFileName()
 * 
 *      getInvokingClassName()
 *      getInvokingMethodName()
 *      getInvokingFileName()
 *
 * Nb. Using StackTrace's to get this info is expensive. There are more optimised ways to obtain
 * method names. See other stackoverflow posts eg. https://stackoverflow.com/questions/421280/in-java-how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection/2924426#2924426
 *
 * 29/09/2012 (lem) - added methods to return (1) fully qualified names and (2) invoking class/method names
 */
package com.stackoverflow.util;

public class StackTraceInfo
{
    /* (Lifted from virgo47's stackoverflow answer) */
    private static final int CLIENT_CODE_STACK_INDEX;

    static {
        // Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
        int i = 0;
        for (StackTraceElement ste: Thread.currentThread().getStackTrace())
        {
            i++;
            if (ste.getClassName().equals(StackTraceInfo.class.getName()))
            {
                break;
            }
        }
        CLIENT_CODE_STACK_INDEX = i;
    }

    public static String getCurrentMethodName()
    {
        return getCurrentMethodName(1);     // making additional overloaded method call requires +1 offset
    }

    private static String getCurrentMethodName(int offset)
    {
        return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getMethodName();
    }

    public static String getCurrentClassName()
    {
        return getCurrentClassName(1);      // making additional overloaded method call requires +1 offset
    }

    private static String getCurrentClassName(int offset)
    {
    return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getClassName();
    }

    public static String getCurrentFileName()
    {
        return getCurrentFileName(1);     // making additional overloaded method call requires +1 offset
    }

    private static String getCurrentFileName(int offset)
    {
        String filename = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getFileName();
        int lineNumber = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getLineNumber();

        return filename + ":" + lineNumber;
    }

    public static String getInvokingMethodName()
    {
        return getInvokingMethodName(2); 
    }

    private static String getInvokingMethodName(int offset)
    {
        return getCurrentMethodName(offset + 1);    // re-uses getCurrentMethodName() with desired index
    }

    public static String getInvokingClassName()
    {
        return getInvokingClassName(2); 
    }

    private static String getInvokingClassName(int offset)
    {
        return getCurrentClassName(offset + 1);     // re-uses getCurrentClassName() with desired index
    }

    public static String getInvokingFileName()
    {
        return getInvokingFileName(2); 
    }

    private static String getInvokingFileName(int offset)
    {
        return getCurrentFileName(offset + 1);     // re-uses getCurrentFileName() with desired index
    }

    public static String getCurrentMethodNameFqn()
    {
        return getCurrentMethodNameFqn(1);
    }

    private static String getCurrentMethodNameFqn(int offset)
    {
        String currentClassName = getCurrentClassName(offset + 1);
        String currentMethodName = getCurrentMethodName(offset + 1);

        return currentClassName + "." + currentMethodName ;
    }

    public static String getCurrentFileNameFqn()
    {
        String CurrentMethodNameFqn = getCurrentMethodNameFqn(1);
        String currentFileName = getCurrentFileName(1);

        return CurrentMethodNameFqn + "(" + currentFileName + ")";
    }

    public static String getInvokingMethodNameFqn()
    {
        return getInvokingMethodNameFqn(2);
    }

    private static String getInvokingMethodNameFqn(int offset)
    {
        String invokingClassName = getInvokingClassName(offset + 1);
        String invokingMethodName = getInvokingMethodName(offset + 1);

        return invokingClassName + "." + invokingMethodName;
    }

    public static String getInvokingFileNameFqn()
    {
        String invokingMethodNameFqn = getInvokingMethodNameFqn(2);
        String invokingFileName = getInvokingFileName(2);

        return invokingMethodNameFqn + "(" + invokingFileName + ")";
    }
}

현재 메소드를 호출 한 메소드의 이름을 얻으려면 다음을 사용할 수 있습니다.

new Exception("is not thrown").getStackTrace()[1].getMethodName()

이것은 내 안드로이드 폰뿐만 아니라 MacBook에서도 작동합니다.

나는 또한 시도했다 :

Thread.currentThread().getStackTrace()[1]

그러나 안드로이드는 "getStackTrace"를 반환합니다.

Thread.currentThread().getStackTrace()[2]

근데 MacBook에서 틀린 답을 얻습니다


Util.java :

public static String getCurrentClassAndMethodNames() {
    final StackTraceElement e = Thread.currentThread().getStackTrace()[2];
    final String s = e.getClassName();
    return s.substring(s.lastIndexOf('.') + 1, s.length()) + "." + e.getMethodName();
}

SomeClass.java :

public class SomeClass {
    public static void main(String[] args) {
        System.out.println(Util.getCurrentClassAndMethodNames()); // output: SomeClass.main
    }
}

이것은 StackWalkerJava 9부터 사용할 수 있습니다 .

public static String getCurrentMethodName() {
    return StackWalker.getInstance()
                      .walk(s -> s.skip(1).findFirst())
                      .get()
                      .getMethodName();
}

public static String getCallerMethodName() {
    return StackWalker.getInstance()
                      .walk(s -> s.skip(2).findFirst())
                      .get()
                      .getMethodName();
}

StackWalker는 게 으르도록 설계되었으므로 Thread.getStackTrace전체 호출 스택에 대한 배열을 열심히 만드는 것보다 더 효율적일 수 있습니다 . 자세한 내용은 JEP를 참조하십시오.


String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);

대체 방법은 예외를 생성하지만 예외는 발생시키지 않고 스택 추적 데이터를 가져 오는 해당 객체를 사용하는 것입니다. 엔 클로징 방법은 일반적으로 JVM이 해당 정보를 저장하는 한 다른 방법 에서처럼 인덱스 0에 있기 때문입니다. 위에 언급했듯이. 그러나 가장 저렴한 방법은 아닙니다.

에서 Throwable.getStackTrace () (이 적어도 자바 5 이후 같은되었습니다)

배열의 0 번째 요소 (배열의 길이가 0이 아닌 것으로 가정)는 스택의 맨 위를 나타내며, 이는 시퀀스에서 마지막 메소드 호출입니다. 일반적 으로이 던지기 가능 항목을 작성하여 던지는 지점입니다.

아래의 스 니펫은 클래스가 정적이 아닌 것으로 가정합니다 (getClass () 때문에).

System.out.printf("Class %s.%s\n", getClass().getName(), new Exception("is not thrown").getStackTrace()[0].getMethodName());

이것을 사용하는 솔루션이 있습니다 (Android에서)

/**
 * @param className       fully qualified className
 *                        <br/>
 *                        <code>YourClassName.class.getName();</code>
 *                        <br/><br/>
 * @param classSimpleName simpleClassName
 *                        <br/>
 *                        <code>YourClassName.class.getSimpleName();</code>
 *                        <br/><br/>
 */
public static void getStackTrace(final String className, final String classSimpleName) {
    final StackTraceElement[] steArray = Thread.currentThread().getStackTrace();
    int index = 0;
    for (StackTraceElement ste : steArray) {
        if (ste.getClassName().equals(className)) {
            break;
        }
        index++;
    }
    if (index >= steArray.length) {
        // Little Hacky
        Log.w(classSimpleName, Arrays.toString(new String[]{steArray[3].getMethodName(), String.valueOf(steArray[3].getLineNumber())}));
    } else {
        // Legitimate
        Log.w(classSimpleName, Arrays.toString(new String[]{steArray[index].getMethodName(), String.valueOf(steArray[index].getLineNumber())}));
    }
}

현재 실행중인 메소드의 이름을 얻는 의도가 무엇인지 모르겠지만, 디버깅 목적으로 만 사용되는 경우 "logback"과 같은 로깅 프레임 워크가 도움이 될 수 있습니다. 예를 들어, 로그 백에서 로깅 구성에 "% M"패턴사용 하기 만하면 됩니다. 그러나 성능이 저하 될 수 있으므로주의해서 사용해야합니다.


알고 싶은 이름의 메소드가 junit 테스트 메소드 인 경우 junit TestName 규칙을 사용할 수 있습니다. https://stackoverflow.com/a/1426730/3076107


나는 maklemenz의 대답을 약간 다시 작성했습니다 .

private static Method m;

static {
    try {
        m = Throwable.class.getDeclaredMethod(
            "getStackTraceElement",
            int.class
        );
    }
    catch (final NoSuchMethodException e) {
        throw new NoSuchMethodUncheckedException(e);
    }
    catch (final SecurityException e) {
        throw new SecurityUncheckedException(e);
    }
}


public static String getMethodName(int depth) {
    StackTraceElement element;

    final boolean accessible = m.isAccessible();
    m.setAccessible(true);

    try {
        element = (StackTraceElement) m.invoke(new Throwable(), 1 + depth);
    }
    catch (final IllegalAccessException e) {
        throw new IllegalAccessUncheckedException(e);
    }
    catch (final InvocationTargetException e) {
        throw new InvocationTargetUncheckedException(e);
    }
    finally {
        m.setAccessible(accessible);
    }

    return element.getMethodName();
}

public static String getMethodName() {
    return getMethodName(1);
}


MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();

이 접근법의 문제점 :

class Example {
    FileOutputStream fileOutputStream;

    public Example() {
        //System.out.println("Example.Example()");

        debug("Example.Example()",false); // toggle

        try {
            fileOutputStream = new FileOutputStream("debug.txt");
        } catch (Exception exception) {
             debug(exception + Calendar.getInstance().getTime());
        }
    }

    private boolean was911AnInsideJob() {
        System.out.println("Example.was911AnInsideJob()");
        return true;
    }

    public boolean shouldGWBushBeImpeached(){
        System.out.println("Example.shouldGWBushBeImpeached()");
        return true;
    }

    public void setPunishment(int yearsInJail){
        debug("Server.setPunishment(int yearsInJail=" + yearsInJail + ")",true);
    }
}

그리고 사람들이 사용에 열중하기 전에 System.out.println(...)항상 출력을 리디렉션 할 수 있도록 몇 가지 방법을 만들어야합니다.

    private void debug (Object object) {
        debug(object,true);
    }

    private void dedub(Object object, boolean debug) {
        if (debug) {
            System.out.println(object);

            // you can also write to a file but make sure the output stream
            // ISN'T opened every time debug(Object object) is called

            fileOutputStream.write(object.toString().getBytes());
        }
    }

참고 URL : https://stackoverflow.com/questions/442747/getting-the-name-of-the-currently-executing-method



도와주세요.
반응형