UFO ET IT

자바에서 호출자 클래스를 얻는 방법

ufoet 2021. 1. 9. 10:37
반응형

자바에서 호출자 클래스를 얻는 방법


이 질문에 이미 답변이 있습니다.

메서드의 호출자 클래스를 얻고 싶습니다.

class foo{

  bar();

}

메서드 표시 줄에서 클래스 이름을 foo가져와야하는데이 메서드를 찾았습니다.

Class clazz = sun.reflect.Reflection.getCallerClass(1);

그러나, 비록 getCallerClass입니다 public이클립스 말한다 호출하려고 할 때 :

액세스 제한 : 필요한 라이브러리 C : \ Program Files \ Java \ jre7 \ lib \ rt.jar에 대한 제한으로 인해 Reflection 유형의 getCallerClass () 메서드에 액세스 할 수 없습니다.

다른 선택이 있습니까?


스택 추적을 생성하고 StackTraceElements 의 정보를 사용할 수 있습니다 .

예를 들어 유틸리티 클래스는 호출 클래스 이름을 반환 할 수 있습니다.

public class KDebug {
    public static String getCallerClassName() { 
        StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
        for (int i=1; i<stElements.length; i++) {
            StackTraceElement ste = stElements[i];
            if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf("java.lang.Thread")!=0) {
                return ste.getClassName();
            }
        }
        return null;
     }
}

KDebug.getCallerClassName()에서 전화 bar()하면 "foo".

이제 호출하는 메서드의 클래스를 알고 싶다고 가정합니다 bar(더 흥미롭고 실제로 원하는 것). 이 방법을 사용할 수 있습니다.

public static String getCallerCallerClassName() { 
    StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
    String callerClassName = null;
    for (int i=1; i<stElements.length; i++) {
        StackTraceElement ste = stElements[i];
        if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf("java.lang.Thread")!=0) {
            if (callerClassName==null) {
                callerClassName = ste.getClassName();
            } else if (!callerClassName.equals(ste.getClassName())) {
                return ste.getClassName();
            }
        }
    }
    return null;
 }

디버깅 용입니까? 그렇지 않은 경우 문제에 대한 더 나은 해결책이있을 수 있습니다.


발신자 / 수신자 클래스 이름을 얻으려면 아래 코드를 사용하면 잘 작동합니다.

String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();

StackTrace

이것은 당신이 찾고있는 것에 크게 의존합니다. 그러나 이것은이 객체 내에서 직접이 메소드를 호출 한 클래스와 메소드를 가져와야합니다.

  • 인덱스 0 = 스레드
  • 인덱스 1 =이
  • 인덱스 2 = 직접 발신자, 본인이 될 수 있습니다.
  • 인덱스 3 ... n = 인덱스 2 이하에 도달하기 위해 서로 호출 한 클래스 및 메서드.

클래스 / 메소드 / 파일 이름 :

Thread.currentThread().getStackTrace()[2].getClassName();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();

학급을 위해:

Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())

참고 : Class.forName ()은 런타임이 아닌 ClassNotFoundException을 발생시킵니다. 캐치가 필요합니다.

또한 클래스 자체 내에서 호출을 무시하려는 경우 특정 항목을 확인하는 논리를 사용하여 루프를 추가해야합니다.

같은 것 ... (이 코드를 테스트하지 않았으므로 조심하십시오)

StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for(int i=2;i<stes.length;i++)
  if(!stes[i].getClassName().equals(this.getClass().getName()))
    return stes[i].getClassName();

StackWalker

StackWalker StackFrame

이것은 광범위한 가이드가 아니라 가능성의 예입니다.

각 StackFrame의 클래스를 인쇄합니다 (클래스 참조를 가져옴).

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
    .forEach(frame -> System.out.println(frame.getDeclaringClass()));

같은 일을하지만 먼저 스트림을 List로 수집합니다. 데모 용입니다.

StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
    .walk(stream -> stream.collect(Collectors.toList()))
    .forEach(frame -> System.out.println(frame.getDeclaringClass()));

SecurityManager보호 된 메소드 getClassContext가 있습니다.

SecurityManager를 확장하는 유틸리티 클래스를 생성하여 이에 액세스 할 수 있습니다.

public class CallingClass extends SecurityManager {
    public static final CallingClass INSTANCE = new CallingClass();

    public Class[] getCallingClasses() {
        return getClassContext();
    }
}

CallingClass.INSTANCE.getCallingClasses()호출 클래스를 검색하는 데 사용 합니다.

이 정보를 노출 하는 작은 라이브러리 (면책 조항 : 내) WhoCalled도 있습니다. 가능한 경우 Reflection.getCallerClass를 사용하고 그렇지 않으면 SecurityManager로 돌아갑니다.


나는 이것이 오래된 질문이라는 것을 알고 있지만 질문자가 클래스 이름이 아니라 클래스를 원한다고 믿었습니다. 실제 수업을받을 수있는 작은 방법을 작성했습니다. 그것은 일종의 치열하고 항상 작동하지 않을 수도 있지만 때로는 실제 수업이 필요할 때이 방법을 사용해야 할 것입니다.

/**
     * Get the caller class.
     * @param level The level of the caller class.
     *              For example: If you are calling this class inside a method and you want to get the caller class of that method,
     *                           you would use level 2. If you want the caller of that class, you would use level 3.
     *
     *              Usually level 2 is the one you want.
     * @return The caller class.
     * @throws ClassNotFoundException We failed to find the caller class.
     */
    public static Class getCallerClass(int level) throws ClassNotFoundException {
        StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
        String rawFQN = stElements[level+1].toString().split("\\(")[0];
        return Class.forName(rawFQN.substring(0, rawFQN.lastIndexOf('.')));
    }

이것은 callers 클래스 만 가져 오는 가장 효율적인 방법입니다. 다른 접근 방식은 전체 스택 덤프를 사용하고 클래스 이름 만 제공합니다.

그러나이 클래스 sun.*는 실제로 내부 용입니다. 이것은 다른 Java 플랫폼이나 다른 Java 버전에서도 작동하지 않을 수 있음을 의미합니다. 이것이 문제인지 아닌지를 결정해야합니다.


OP에 발생하는 오류 메시지는 Eclipse 기능 일뿐입니다. 코드를 JVM의 특정 제조업체 (및 버전)에 연결하려는 경우 method를 효과적으로 사용할 수 있습니다 sun.reflect.Reflection.getCallerClass(). 그런 다음 Eclipse 외부에서 코드를 컴파일하거나이 진단을 오류로 간주하지 않도록 구성 할 수 있습니다.

더 나쁜 Eclipse 구성은 다음과 같은 방법으로 모든 오류 발생 을 비활성화 하는 것입니다.

Project Properties/ Java Compiler/ Errors/Warnings/ Enable project specific settings검사 /로 설정 Deprecated and restrited API/ Forbidden reference (access rules)로 설정 Warning하거나 Ignore.

더 나은 Eclipse 구성은 다음과 같은 방법으로 특정 오류 발생을 비활성화하는 것입니다.

Project Properties/ Java Build Path/ Libraries/ JRE System Library확장 / Access rules:선택 / Edit.../ Add.../로 Resolution:설정 Discouraged하거나 Accessible/로 Rule Pattern설정합니다 sun/reflect/Reflection.


Find below a simple example illustrating how to get class and method names.

public static void main(String args[])
   {
      callMe();
   }

   void callMe()
   {
      try
      {
         throw new Exception("Who called me?");
      }
      catch( Exception e )
      {
         System.out.println( "I was called by " + 
                             e.getStackTrace()[1].getClassName() + 
                             "." +
                             e.getStackTrace()[1].getMethodName() + 
                             "()!" );
      }
   }

e has getClassName(), getFileName(), getLineNumber() and getMethodName()...


i am using the following method to get the caller for a specific class from the stacktrace:

package test.log;

public class CallerClassTest {

    public static void main(final String[] args) {
        final Caller caller = new Caller(new Callee());
        caller.execute();
    }

    private static class Caller {

        private final Callee c;

        public Caller(final Callee c) {
            this.c = c;
        }

        void execute() {
            c.call();
        }
    }

    static class Callee {

        void call() {
            System.out.println(getCallerClassName(this.getClass()));
        }
    }

    /**
     * Searches the current threads stacktrace for the class that called the given class. Returns {@code null} if the
     * calling class could not be found.
     * 
     * @param clazz
     *            the class that has been called
     * 
     * @return the caller that called the class or {@code null}
     */
    public static String getCallerClassName(final Class<?> clazz) {
        final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        final String className = clazz.getName();
        boolean classFound = false;
        for (int i = 1; i < stackTrace.length; i++) {
            final StackTraceElement element = stackTrace[i];
            final String callerClassName = element.getClassName();
            // check if class name is the requested class
            if (callerClassName.equals(className)) classFound = true;
            else if (classFound) return callerClassName;
        }
        return null;
    }

}

Since I currently have the same problem here is what I do:

  1. I prefer com.sun.Reflection instead of stackTrace since a stack trace is only producing the name not the class (including the classloader) itself.

  2. The method is deprecated but still around in Java 8 SDK.

// Method descriptor #124 (I)Ljava/lang/Class; (deprecated) // Signature: (I)Ljava/lang/Class<*>; @java.lang.Deprecated public static native java.lang.Class getCallerClass(int arg0);

  1. The method without int argument is not deprecated

// Method descriptor #122 ()Ljava/lang/Class; // Signature: ()Ljava/lang/Class<*>; @sun.reflect.CallerSensitive public static native java.lang.Class getCallerClass();

Since I have to be platform independent bla bla including Security Restrictions, I just create a flexible method:

  1. Check if com.sun.Reflection is available (security exceptions disable this mechanism)

  2. If 1 is yes then get the method with int or no int argument.

  3. If 2 is yes call it.

If 3. was never reached, I use the stack trace to return the name. I use a special result object that contains either the class or the string and this object tells exactly what it is and why.

[Summary] I use stacktrace for backup and to bypass eclipse compiler warnings I use reflections. Works very good. Keeps the code clean, works like a charm and also states the problems involved correctly.

I use this for quite a long time and today I searched a related question so

ReferenceURL : https://stackoverflow.com/questions/11306811/how-to-get-the-caller-class-in-java

반응형