Как получить класс вызывающего в Java

Я хочу получить класс вызывающего метода, т.е.

class foo{

  bar();

}

В панели методов мне нужно получить имя класса fooи я нашел этот метод:

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

Тем не менее, даже если getCallerClass является publicКогда я пытаюсь это назвать, Затмение говорит:

Ограничение доступа: метод getCallerClass() из типа Reflection недоступен из-за ограничения на требуемую библиотеку C:\Program Files\Java\jre7\lib\rt.jar

Есть ли другие варианты?

10 ответов

Вы можете генерировать трассировку стека и использовать информацию в 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;
 }

Это для отладки? Если нет, то может быть лучшее решение вашей проблемы.

Это сильно зависит от того, что вы ищете... Но это должно получить класс и метод, который вызвал этот метод непосредственно в этом объекте.

  • индекс 0 = поток
  • индекс 1 = это
  • индекс 2 = прямой абонент, может быть самостоятельно.
  • index 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();

Просто мысль....

Чтобы получить имя вызывающего / вызываемого класса, используйте приведенный ниже код, он отлично работает для меня.

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

SecurityManager имеет защищенный метод getClassContext

Создав служебный класс, который расширяет SecurityManager, вы можете получить к нему доступ.

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

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

использование CallingClass.INSTANCE.getCallingClasses() чтобы получить вызывающие классы.

Существует также небольшая библиотека ( Disclaimer: mine) 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('.')));
    }

Сообщение об ошибке, с которым сталкивается OP, - это просто функция Eclipse. Если вы хотите привязать свой код к конкретному производителю (и даже к версии) JVM, вы можете эффективно использовать метод 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,

Это самый эффективный способ получить только класс абонентов. Другие подходы занимают весь дамп стека и дают только имя класса.

Тем не менее, этот класс в под sun.* который действительно для внутреннего использования. Это означает, что он может не работать на других платформах Java или даже других версиях Java. Вы должны решить, является ли это проблемой или нет.

Ниже приведен простой пример, иллюстрирующий, как получить имена классов и методов.

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() + 
                             "()!" );
      }
   }

у него есть getClassName(), getFileName(), getLineNumber() а также getMethodName()...

Так как у меня в настоящее время есть та же самая проблема, вот что я делаю:

  1. Я предпочитаю com.sun.Reflection вместо stackTrace, поскольку трассировка стека создает только имя, а не сам класс (включая загрузчик классов).

  2. Этот метод устарел, но все еще используется в Java 8 SDK.

// дескриптор метода #124 (I)Ljava/lang/Class; (устарело) // Подпись: (I) Ljava / lang / Class<*>; @ java.lang.Deprecated общедоступная статическая нативная java.lang.Class getCallerClass(int arg0);

  1. Метод без аргумента int не считается устаревшим

// Дескриптор метода #122 ()Ljava/lang/Class; // Подпись: () Ljava / lang / Class<*>; @ sun.reflect.CallerSensitive public static native java.lang.Class getCallerClass ();

Поскольку я должен быть независимым от платформы, в том числе с учетом ограничений безопасности, я просто создаю гибкий метод:

  1. Проверьте, доступен ли com.sun.Reflection (исключения безопасности отключают этот механизм)

  2. Если 1 - да, тогда получите метод с аргументом int или без аргумента int.

  3. Если 2 - да, позвони.

Если 3. никогда не было достигнуто, я использую трассировку стека, чтобы вернуть имя. Я использую специальный объект результата, который содержит либо класс, либо строку, и этот объект точно говорит, что это такое и почему.

[Резюме] Я использую 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;
    }

}
Другие вопросы по тегам