Как найти вызывающего метода с помощью трассировки стека или отражения?

Мне нужно найти вызывающего метода. Возможно ли использование трассировки стека или отражения?

Просто интересно, а зачем вам это делать?

Juliet 07.01.2009 20:51

У меня есть родительский класс (модель MVC) с событием-уведомителем, и только установщики моих подклассов вызывают этот метод. Я не хочу засорять свой код лишними аргументами. Я бы предпочел позволить методу в родительском классе определить сеттер, который его вызвал.

Sathish 07.01.2009 20:56

@Sathish Похоже, вам стоит переосмыслить этот дизайн

krosenvold 07.01.2009 20:59

Также согласен, что необходим редизайн. Смотрите мой ответ ниже.

Craig P. Motlin 07.01.2009 21:09

@Sathish Я знаю, что это довольно давно, но более распространенный способ сделать это - передать вызывающего объекта в конструктор, чтобы дочерние классы знали вызывающего. Причина в том, что трассировка стека не возвращает фактический экземпляр. Если вы действительно говорите о подклассе, то было бы лучше объявить ваш суперкласс как абстрактный класс и реализовать общий код в родительском классе, который зависит от данных подкласса, чтобы использовать абстрактную функцию для получения этих данных из подкласса.

Armand 07.03.2014 21:32

@Juliet В рамках рефакторинга большой части кода я недавно изменил метод, который используется во многих вещах. Существует определенный способ определить, правильно ли код использовал новый метод, поэтому я печатал класс и номер строки, которые вызывали его в этих случаях. Вне регистрации я не вижу реальной цели для чего-то подобного. Хотя сейчас я как бы хочу написать API, которые выдают DontNameYourMethodFooException, если вызывающий метод называется foo.

Cruncher 17.05.2014 00:06

Я считаю, что возможность получить вызывающий мой метод бесценный инструмент отладки: именно так поиск в Интернете привел меня сюда. Если мой метод вызывается из нескольких мест, вызывается ли он из нужного места в нужное время? Вне отладки или регистрации полезность, вероятно, в лучшем случае ограничена, как упоминает @Cruncher.

Ogre Psalm33 19.08.2014 16:11
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
403
7
278 738
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

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

Я делал это раньше для утилиты ведения журнала в приложении, где производительность не имела большого значения (на самом деле производительность редко имеет значение вообще - если вы показываете результат для такого действия, как быстрое нажатие кнопки).

Это было до того, как вы смогли получить трассировку стека, исключения имели только .printStackTrace (), поэтому мне пришлось перенаправить System.out в поток моего собственного создания, затем (new Exception ()). PrintStackTrace (); Перенаправьте System.out обратно и проанализируйте поток. Веселая штука.

Прохладный; тебе не нужно его бросать?

krosenvold 07.01.2009 21:00

Нет, по крайней мере, так я помню, я не делал этого несколько лет, но я почти уверен, что создание исключения - это просто создание объекта, а выброс исключения ничего не делает с ним, кроме передачи это в предложение catch ().

Bill K 07.01.2009 21:05

Аккуратный. Я был склонен бросить его, чтобы смоделировать фактическое исключение.

Sathish 07.01.2009 21:13

Нет, начиная с Java 5 существует метод в Thread для получения текущего стека в виде массива StackTraceElements; это все еще недешево, но дешевле старого решения для синтаксического анализа исключений.

Lawrence Dol 08.01.2009 11:40

@Software Monkey Хотя я уверен, что это более уместно, почему вы говорите, что это дешевле? Я предполагаю, что будет использоваться тот же механизм, а если нет, зачем делать его медленнее, если он делает то же самое?

Bill K 08.01.2009 20:11

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

Lawrence Dol 18.04.2012 23:28
Ответ принят как подходящий
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()

Согласно Javadocs:

The last element of the array represents the bottom of the stack, which is the least recent method invocation in the sequence.

StackTraceElement содержит getClassName(), getFileName(), getLineNumber() и getMethodName().

Вам придется поэкспериментировать, чтобы определить, какой индекс вам нужен (вероятно, stackTraceElements[1] или [2]).

Я должен отметить, что getStackTrace () по-прежнему создает Exception, так что это не на самом деле быстрее - просто удобнее.

Michael Myers 07.01.2009 21:08

Обратите внимание, что этот метод не выдаст вам вызывающего, а только тип звонящего. У вас не будет ссылки на объект, вызывающий ваш метод.

Joachim Sauer 07.01.2009 21:30

@mmyers: Приятно знать, но я думаю, что это лучше выражает намерение, чем создание исключения выброса.

Lawrence Dol 08.01.2009 11:44

Просто примечание, но на 1.5 JVM Thread.currentThread (). GetStackTrace () кажется намного медленнее, чем создание нового Exception () (примерно в 3 раза медленнее). Но, как уже отмечалось, вам в любом случае не следует использовать подобный код в областях, критичных к производительности. ;) JVM 1.6 только кажется примерно на 10% медленнее и, как сказал Software Monkey, выражает намерение лучше, чем способ "нового исключения".

GaZ 07.07.2009 18:42

Но GaZ подразумевает, что Thread.currentThread действительно очень дорогой метод (который здесь вызывается дважды, единственный другой выполняемый код - это проверка, равен ли текущий поток этому потоку). Честно говоря, мне было трудно в это поверить.

Eelco 02.02.2010 08:22

@Eelco Thread.currentThread () дешевый. Thread.getStackTrace () является дорогостоящим, потому что, в отличие от Throwable.fillInStackTrace (), нет гарантии, что метод вызывается тем же потоком, который он исследует, поэтому JVM должна создать «точку безопасности» - блокировку кучи и стека. См. Этот отчет об ошибке: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302

David Moles 09.12.2011 03:30

@JoachimSauer знаете ли вы способ получить ссылку на объект, вызывающий метод?

jophde 20.01.2015 01:31

Помечено как «не ответ», потому что, как указал @JoachimSauer, это фактически не дает вам объект, который вызвал.

kontur 16.02.2016 13:25

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

Shraddheya Shendre 21.12.2016 10:07

@ shraddheya-shendre Хорошо. А как насчет параметров метода? Класс может содержать методы умножения с тем же именем.

Alexiuscrow 13.11.2017 21:31

@Alexiuscrow может быть не так полезен во время выполнения, и он все равно не даст вам параметры, но если вы делаете это для ведения журнала и просто хотите знать, какой перегруженный метод был вызван, вы можете использовать метод getLineNumber () чтобы точно указать, какой метод вызывается.

Dylan Smith 14.08.2018 16:50

Похоже, вы пытаетесь избежать передачи в метод ссылки на this. Передача this намного лучше, чем поиск вызывающего по текущей трассировке стека. Еще лучше рефакторинг в сторону объектно-ориентированного дизайна. Вам не нужно знать вызывающего абонента. При необходимости передайте объект обратного вызова.

++ Знание вызывающего - слишком много информации. При необходимости вы можете передать интерфейс, но есть большая вероятность, что потребуется серьезный рефакторинг. @satish должен опубликовать свой код и позволить нам повеселиться с ним :)

Bill K 07.01.2009 21:19

Веские причины для этого существуют. У меня было несколько случаев, когда я находил это полезным, например, во время тестирования.

Eelco 02.02.2010 08:23

@chillenious Я знаю :) Я сам создал такой метод, как LoggerFactory.getLogger(MyClass.class), где мне не нужно было передавать литерал класса. Это по-прежнему редко бывает правильным.

Craig P. Motlin 02.02.2010 18:05

В целом это хороший совет, но он не отвечает на вопрос.

Navin 16.11.2013 00:38

Конкретным примером того, когда может быть ПРАВИЛЬНОЕ дизайнерское решение получить информацию о вызывающей стороне, является реализация интерфейса .NET INotifyPropertyChanged. Хотя этого конкретного примера нет в Java, та же проблема может проявиться при попытке смоделировать поля / геттеры как строки для Reflection.

Chris Kerekes 15.01.2016 20:13
     /**
       * 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 = new Throwable().getStackTrace();

        //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
        return ste[ste.length - depth].getMethodName();
      }

Например, если вы пытаетесь получить строку вызывающего метода для целей отладки, вам нужно пройти мимо класса Utility, в котором вы кодируете эти статические методы:
(старый код java1.4, просто чтобы проиллюстрировать возможное использование StackTraceElement)

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils". <br />
          * From the Stack Trace.
          * @return "[class#method(line)]: " (never empty, first class past StackTraceUtils)
          */
        public static String getClassMethodLine()
        {
            return getClassMethodLine(null);
        }

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass. <br />
          * Allows to get past a certain class.
          * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
          * @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils)
          */
        public static String getClassMethodLine(final Class aclass)
        {
            final StackTraceElement st = getCallingStackTraceElement(aclass);
            final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber()
            +")] <" + Thread.currentThread().getName() + ">: ";
            return amsg;
        }

     /**
       * Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass. <br />
       * Stored in array of the callstack. <br />
       * Allows to get past a certain class.
       * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
       * @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils)
       * @throws AssertionFailedException if resulting statckTrace is null (RuntimeException)
       */
      public static StackTraceElement getCallingStackTraceElement(final Class aclass)
      {
        final Throwable           t         = new Throwable();
        final StackTraceElement[] ste       = t.getStackTrace();
        int index = 1;
        final int limit = ste.length;
        StackTraceElement   st        = ste[index];
        String              className = st.getClassName();
        boolean aclassfound = false;
        if (aclass == null)
        {
            aclassfound = true;
        }
        StackTraceElement   resst = null;
        while(index < limit)
        {
            if (shouldExamine(className, aclass) == true)
            {
                if (resst == null)
                {
                    resst = st;
                }
                if (aclassfound == true)
                {
                    final StackTraceElement ast = onClassfound(aclass, className, st);
                    if (ast != null)
                    {
                        resst = ast;
                        break;
                    }
                }
                else
                {
                    if (aclass != null && aclass.getName().equals(className) == true)
                    {
                        aclassfound = true;
                    }
                }
            }
            index = index + 1;
            st        = ste[index];
            className = st.getClassName();
        }
        if (resst == null) 
        {
            //Assert.isNotNull(resst, "stack trace should null"); //NO OTHERWISE circular dependencies 
            throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$
        }
        return resst;
      }

      static private boolean shouldExamine(String className, Class aclass)
      {
          final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith("LogUtils"
            ) == false || (aclass !=null && aclass.getName().endsWith("LogUtils")));
          return res;
      }

      static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st)
      {
          StackTraceElement   resst = null;
          if (aclass != null && aclass.getName().equals(className) == false)
          {
              resst = st;
          }
          if (aclass == null)
          {
              resst = st;
          }
          return resst;
      }

Мне нужно было что-то, что работает с Java 1.4, и этот ответ был очень полезным! Спасибо!

RGO 07.03.2014 10:03

Этот метод делает то же самое, но немного проще и, возможно, немного более производительно, и, если вы используете отражение, он автоматически пропускает эти кадры. Единственная проблема заключается в том, что он может отсутствовать в JVM сторонних разработчиков, хотя он включен в классы времени выполнения JRockit 1.4 -> 1.6. (Дело в том, что это не класс общественный).

sun.reflect.Reflection

    /** Returns the class of the method <code>realFramesToSkip</code>
        frames up the stack (zero-based), ignoring frames associated
        with java.lang.reflect.Method.invoke() and its implementation.
        The first frame is that associated with this method, so
        <code>getCallerClass(0)</code> returns the Class object for
        sun.reflect.Reflection. Frames associated with
        java.lang.reflect.Method.invoke() and its implementation are
        completely ignored and do not count toward the number of "real"
        frames skipped. */
    public static native Class getCallerClass(int realFramesToSkip);

Что касается того, каким должно быть значение realFramesToSkip, версии java.lang.System Sun 1.5 и 1.6 VM, существует метод, защищенный пакетом, getCallerClass (), который вызывает sun.reflect.Reflection.getCallerClass(3), но в моем вспомогательном служебном классе я использовал 4, поскольку есть добавленный фрейм вызов вспомогательного класса.

Использование классов реализации JVM - плохая идея В самом деле.

Lawrence Dol 08.01.2009 11:42

Принято к сведению. Я указал, что это не общедоступный класс, а защищенный метод getCallerClass () в java.lang.System присутствует во всех 1,5+ виртуальных машинах, на которые я смотрел, включая IBM, JRockit и Sun, но ваше утверждение консервативно звучит .

Nicholas 08.01.2009 22:05

@Software Monkey, ну как обычно "все зависит". Выполнение чего-то подобного для помощи в отладке или ведении журнала тестов - особенно если это никогда не заканчивается в производственном коде - или если целью развертывания является строго ПК разработчика, вероятно, будет нормально. Любой, кто по-прежнему думает иначе даже в таких случаях: вам нужно будет объяснить причину «В самом деле плохая идея» лучше, чем просто сказать, что это плохо ...

user796820 18.03.2013 01:29

Кроме того, по аналогичной логике вы также можете утверждать, что всякий раз, когда вы используете специфичную для Hibernate функцию, не совместимую с JPA, это всегда «плохая идея В самом деле». Или, если вы собираетесь использовать специфические для Oracle функции, которых нет в других базах данных, это «плохая идея В самом деле». Конечно, это более безопасный образ мышления и определенно хороший совет для определенных целей, но он автоматически отбрасывает полезные инструменты только потому, что они не работают с конфигурацией программного обеспечения, которую вы, ээ .. вообще не использую? Это слишком негибко и немного глупо.

user796820 18.03.2013 01:39

@Perce для решений здесь и сейчас, он работает хорошо. Проблема возникает, если - по какой-либо причине - такие решения проникают в производственный код, и в конечном итоге требования меняются (например, запускаются на Azul или Google Application Engine), и тогда у обслуживающей бригады возникает серьезная проблема, если код не запускается. Классы, зависящие от продавца, - это бомбы замедленного действия.

Thorbjørn Ravn Andersen 25.06.2013 08:20

Незащищенное использование классов, специфичных для поставщика, представит более высокую вероятность проблем, но следует определить путь постепенного ухудшения, если рассматриваемый класс отсутствует (или запрещен по какой-либо причине). На мой взгляд, политика категорического отказа от использования каких-либо классов, специфичных для конкретных поставщиков, немного наивна. Поищите в исходном коде некоторых библиотек, которые вы используете в продакшене, и посмотрите, делает ли какая-нибудь из них это. (sun.misc. Возможно небезопасно?)

Nicholas 25.06.2013 18:03

Альтернативное решение можно найти в комментарии к этот запрос на улучшение. Он использует метод getClassContext() пользовательского SecurityManager и кажется более быстрым, чем метод трассировки стека.

Следующая программа проверяет скорость различных предложенных методов (самый интересный бит находится во внутреннем классе SecurityManagerMethod):

/**
 * Test the speed of various methods for getting the caller class name
 */
public class TestGetCallerClassName {

  /**
   * Abstract class for testing different methods of getting the caller class name
   */
  private static abstract class GetCallerClassNameMethod {
      public abstract String getCallerClassName(int callStackDepth);
      public abstract String getMethodName();
  }

  /**
   * Uses the internal Reflection class
   */
  private static class ReflectionMethod extends GetCallerClassNameMethod {
      public String getCallerClassName(int callStackDepth) {
          return sun.reflect.Reflection.getCallerClass(callStackDepth).getName();
      }

      public String getMethodName() {
          return "Reflection";
      }
  }

  /**
   * Get a stack trace from the current thread
   */
  private static class ThreadStackTraceMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return Thread.currentThread().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Current Thread StackTrace";
      }
  }

  /**
   * Get a stack trace from a new Throwable
   */
  private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod {

      public String getCallerClassName(int callStackDepth) {
          return new Throwable().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Throwable StackTrace";
      }
  }

  /**
   * Use the SecurityManager.getClassContext()
   */
  private static class SecurityManagerMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return mySecurityManager.getCallerClassName(callStackDepth);
      }

      public String getMethodName() {
          return "SecurityManager";
      }

      /** 
       * A custom security manager that exposes the getClassContext() information
       */
      static class MySecurityManager extends SecurityManager {
          public String getCallerClassName(int callStackDepth) {
              return getClassContext()[callStackDepth].getName();
          }
      }

      private final static MySecurityManager mySecurityManager =
          new MySecurityManager();
  }

  /**
   * Test all four methods
   */
  public static void main(String[] args) {
      testMethod(new ReflectionMethod());
      testMethod(new ThreadStackTraceMethod());
      testMethod(new ThrowableStackTraceMethod());
      testMethod(new SecurityManagerMethod());
  }

  private static void testMethod(GetCallerClassNameMethod method) {
      long startTime = System.nanoTime();
      String className = null;
      for (int i = 0; i < 1000000; i++) {
          className = method.getCallerClassName(2);
      }
      printElapsedTime(method.getMethodName(), startTime);
  }

  private static void printElapsedTime(String title, long startTime) {
      System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms.");
  }
}

Пример вывода моего MacBook с процессором Intel Core 2 Duo 2,4 ГГц с Java 1.6.0_17:

Reflection: 10.195 ms.
Current Thread StackTrace: 5886.964 ms.
Throwable StackTrace: 4700.073 ms.
SecurityManager: 1046.804 ms.

Внутренний метод отражения на много быстрее, чем другие. Получить трассировку стека из вновь созданного Throwable быстрее, чем из текущего Thread. А среди не внутренних способов найти класс вызывающего абонента, кастомный SecurityManager кажется самым быстрым.

Обновлять

Как указывает Lyomi в этот комментарий, метод sun.reflect.Reflection.getCallerClass() был отключен по умолчанию в обновлении 40 Java 7 и полностью удален в Java 8. Подробнее об этом читайте в эта проблема в базе данных ошибок Java.

Обновление 2

Как обнаружил замби, Oracle был вынужден отказаться от изменения, который удалил sun.reflect.Reflection.getCallerClass(). Он по-прежнему доступен в Java 8 (но устарел).

Обновление 3

3 года спустя: обновление сроков с текущей JVM.

> java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
> java TestGetCallerClassName
Reflection: 0.194s.
Current Thread StackTrace: 3.887s.
Throwable StackTrace: 3.173s.
SecurityManager: 0.565s.

Спасибо за сравнение по скорости. Неужели StackTrace действительно такой медленный?

Kevin Parker 22.06.2012 00:44

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

Johan Kaving 27.06.2012 15:09

Для меня удаление отражения из моего проекта привело к увеличению скорости в 10 раз.

Kevin Parker 27.06.2012 18:20

Да, отражение в целом происходит медленно (см., Например, stackoverflow.com/questions/435553/java-reflection-performan ce), но в этом конкретном случае использование внутреннего класса sun.reflect.Reflection является самым быстрым.

Johan Kaving 28.06.2012 16:39

Это не имеет значения для вашего теста, но для правильности я считаю, что реализация менеджера безопасности должна где-то делать (callStackDepth + 1), поскольку использование подкласса SecurityManager добавляет дополнительный уровень глубины стека.

Jeremy Huiskamp 31.07.2012 23:19

На самом деле в этом нет необходимости. Вы можете проверить это, изменив приведенный выше код, чтобы напечатать возвращенное имя класса (и я предлагаю уменьшить количество циклов до 1). Вы увидите, что все методы возвращают одно и то же имя класса - TestGetCallerClassName.

Johan Kaving 07.08.2012 10:07

getClasscontext совершенно потрясающий. И на андроиде тоже работает! (на Android это в 10 раз быстрее, чем getStackTrace)

njzk2 14.11.2012 21:23

Обратите внимание, что подход отражения использует классы, зависящие от поставщика. Он будет работать только на JVM на базе Oracle. Это может быть важно, а может и нет.

Thorbjørn Ravn Andersen 25.06.2013 08:13

getCallerClass устарел и будет удален в 7u40 .. печально :(

lyomi 05.08.2013 06:02

"getCallerClass устарел" Влияет ли это как на способ отражения, так и на способ SecurityManager?

Daniel Ryan 26.06.2014 01:32

Похоже, он пока останется: bugs.java.com/view_bug.do?bug_id=8021946

Daniel Ryan 26.06.2014 05:51

Да, и он все еще доступен (но не рекомендуется) в Java 8. Это не влияет на способ SecurityManager.

Johan Kaving 27.06.2014 15:06

@JohanKaving, привет, могу ли я узнать, как я могу получить имя вызывающего метода с помощью SecurityManager? В своем ответе вы возвращаете жестко запрограммированное имя метода. getClassContext()[callStackDepth].getName(); получает имя класса. Я хочу получить фактическое имя метода вызова с помощью диспетчера безопасности. Это возможно? Спасибо!

iamkenos 31.05.2017 12:08

@iamkenos Я думаю, вам нужно будет использовать трассировку стека, чтобы получить имя вызывающего метода. Можно сделать new Throwable().getStackTrace()[callStackDepth].getMethodName()

Johan Kaving 19.06.2017 10:41

@ njzk2 SecurityManager.getClassContext() может возвращать null на Android. Проверено на SDK версий 16 и 22.

Andi 08.07.2018 00:56

Вот часть кода, который я сделал на основе подсказок, показанных в этом разделе. Надеюсь, поможет.

(Не стесняйтесь вносить любые предложения по улучшению этого кода, сообщите мне, пожалуйста)

Счетчик:

public class InstanceCount{
    private static Map<Integer, CounterInstanceLog> instanceMap = new HashMap<Integer, CounterInstanceLog>();
private CounterInstanceLog counterInstanceLog;


    public void count() {
        counterInstanceLog= new counterInstanceLog();
    if (counterInstanceLog.getIdHashCode() != 0){
    try {
        if (instanceMap .containsKey(counterInstanceLog.getIdHashCode())) {
         counterInstanceLog= instanceMap .get(counterInstanceLog.getIdHashCode());
    }

    counterInstanceLog.incrementCounter();

            instanceMap .put(counterInstanceLog.getIdHashCode(), counterInstanceLog);
    }

    (...)
}

И объект:

public class CounterInstanceLog{
    private int idHashCode;
    private StackTraceElement[] arrayStackTraceElements;
    private int instanceCount;
    private String callerClassName;

    private StackTraceElement getProjectClasses(int depth) {
      if (depth< 10){
        getCallerClassName(sun.reflect.Reflection.getCallerClass(depth).getName());
        if (getCallerClassName().startsWith("com.yourproject.model")){
            setStackTraceElements(Thread.currentThread().getStackTrace());
            setIdHashCode();
        return arrayStackTraceElements[depth];
        }
        //+2 because one new item are added to the stackflow
        return getProjectClasses(profundidade+2);           
      }else{
        return null;
      }
    }

    private void setIdHashCode() {
        if (getNomeClasse() != null){
            this.idHashCode = (getCallerClassName()).hashCode();
        }
    }

    public void incrementaContador() {
    this.instanceCount++;
}

    //getters and setters

    (...)



}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

class DBConnection {
    String createdBy = null;

    DBConnection(Throwable whoCreatedMe) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(os);
        whoCreatedMe.printStackTrace(pw);
        try {
            createdBy = os.toString();
            pw.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class ThrowableTest {

    public static void main(String[] args) {

        Throwable createdBy = new Throwable(
                "Connection created from DBConnectionManager");
        DBConnection conn = new DBConnection(createdBy);
        System.out.println(conn.createdBy);
    }
}

ИЛИ ЖЕ

public static interface ICallback<T> { T doOperation(); }


public class TestCallerOfMethod {

    public static <T> T callTwo(final ICallback<T> c){
        // Pass the object created at callee to the caller
        // From the passed object we can get; what is the callee name like below.
        System.out.println(c.getClass().getEnclosingMethod().getName());
        return c.doOperation();
    }

    public static boolean callOne(){
        ICallback callBackInstance = new ICallback(Boolean){
            @Override
            public Boolean doOperation() 
            {
                return true;
            }
        };
        return callTwo(callBackInstance);
    }

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

используйте этот метод: -

 StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
 stackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
 System.out.println(e.getMethodName());

Вызывающий пример кода метода находится здесь: -

public class TestString {

    public static void main(String[] args) {
        TestString testString = new TestString();
        testString.doit1();
        testString.doit2();
        testString.doit3();
        testString.doit4();
    }

    public void doit() {
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
        System.out.println(e.getMethodName());
    }

    public void doit1() {
        doit();
    }

    public void doit2() {
        doit();
    }

    public void doit3() {
        doit();
    }

    public void doit4() {
        doit();
    }
}
private void parseExceptionContents(
      final Exception exception,
      final OutputStream out)
   {
      final StackTraceElement[] stackTrace = exception.getStackTrace();
      int index = 0;
      for (StackTraceElement element : stackTrace)
      {
         final String exceptionMsg =
              "Exception thrown from " + element.getMethodName()
            + " in class " + element.getClassName() + " [on line number "
            + element.getLineNumber() + " of file " + element.getFileName() + "]";
         try
         {
            out.write((headerLine + newLine).getBytes());
            out.write((headerTitlePortion + index++ + newLine).getBytes() );
            out.write((headerLine + newLine).getBytes());
            out.write((exceptionMsg + newLine + newLine).getBytes());
            out.write(
               ("Exception.toString: " + element.toString() + newLine).getBytes());
         }
         catch (IOException ioEx)
         {
            System.err.println(
                 "IOException encountered while trying to write "
               + "StackTraceElement data to provided OutputStream.\n"
               + ioEx.getMessage() );
         }
      }
   }

Один лайнер:

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

Обратите внимание, что вам может потребоваться заменить 2 на 1.

Небольшая поправка для Android. Thread.currentThread().getStackTrace()[3].getMethodName() возвращает имя метода вызывающего абонента

Qamar 07.12.2020 14:05

Java 9 - JEP 259: API обхода стека

JEP 259 предоставляет эффективный стандартный API для обхода стека, который позволяет легко фильтровать информацию в трассировках стека и выполнять отложенный доступ к ней. До Stack-Walking API распространенными способами доступа к фреймам стека были:

Throwable::getStackTrace and Thread::getStackTrace return an array of StackTraceElement objects, which contain the class name and method name of each stack-trace element.

SecurityManager::getClassContext is a protected method, which allows a SecurityManager subclass to access the class context.

JDK-internal sun.reflect.Reflection::getCallerClass method which you shouldn't use anyway

Использование этих API обычно неэффективно:

These APIs require the VM to eagerly capture a snapshot of the entire stack, and they return information representing the entire stack. There is no way to avoid the cost of examining all the frames if the caller is only interested in the top few frames on the stack.

Чтобы найти класс непосредственного вызывающего абонента, сначала получите StackWalker:

StackWalker walker = StackWalker
                           .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

Затем либо позвоните на getCallerClass():

Class<?> callerClass = walker.getCallerClass();

или walkStackFrame и получить первый предшествующий StackFrame:

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());

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