Cuatro formas de obtener el nombre de la clase o método actual en Java (información de la pila de llamadas)

En Java, es posible determinar en tiempo de ejecución quién invocó el método actual, es decir, obtener la cadena de llamaads. Existen al menos cuatro enfoques principales: mediante Thread, Throwable y SecurityManager. A continuación se detalla cada uno con ejemplos y explicaciones.

1. Usando Thread.getAllStackTraces()

Este método devuelve un Map<Thread, StackTraceElement[]> con las trazas de pila de todos los hilos. Para obtener la pila del hilo actual, se utiliza Thread.currentThread() como clave. Cada StackTraceElement proporciona información como el nombre de la clase, el objeto Class y el número de línea.

public class A {
    public static void main(String[] args) {
        B.print();
    }
}

class B {
    public static void print() {
        System.out.println("Llamando a B#print");
        C.print();
    }
}

class C {
    public static void print() {
        System.out.println("Llamando a C#print");
        Map<Thread, StackTraceElement[]> allTraces = Thread.getAllStackTraces();
        StackTraceElement[] traceElements = allTraces.get(Thread.currentThread());
        System.out.println(Arrays.toString(traceElements));
    }
}

En la salida, el elemento en el índice 4 corresponde a main. Los índices 0 y 1 muestran llamadas internas del JDK, debido a la ejecución de getAllStackTraces().

2. Usando Thread.currentThread().getStackTrace()

Alternativa más directa: se obtiene el hilo actual con Thread.currentThread() y se invoca getStackTrace(). Itnernamente, ambos métodos (el primero y este) llaman al método nativo private native static StackTraceElement[][] dumpThreads(Thread[] threads). La traza resultante también incluye elementos del sistema (índice 0).

public class A {
    public static void main(String[] args) {
        B.print();
        System.out.println(Arrays.toString(Thread.currentThread().getStackTrace()));
    }
}

class B {
    public static void print() {
        System.out.println("Llamando a B#print");
        C.print();
    }
}

class C {
    public static void print() {
        StackTraceElement[] elements = Thread.currentThread().getStackTrace();
        System.out.println(Arrays.toString(elements));
    }
}

3. Usando new RuntimeException().getStackTrace()

Cualquier subclase de Throwable (incluyedno el mismo) puede generar una traza de pila. Este enfoque es utilizado, por ejemplo, en SpringApplication.deduceMainApplicationClass() para identificar la clase principal. Se itera sobre el arreglo para filtrar por nombre de método o tipo.

public class A {
    public static void main(String[] args) {
        B.print();
    }
}

class B {
    public static void print() {
        System.out.println("Llamando a B#print");
        C.print();
    }
}

class C {
    public static void print() {
        StackTraceElement[] trace = new RuntimeException().getStackTrace();
        System.out.println(Arrays.toString(trace));
    }
}

4. Usando SecurityManager (solo información de clases)

El método protegido getClassContext() de SecurityManager devuelve un arreglo de objetos Class<?>, sin incluir números de línea ni nombres de métodos. Para acceder a él, se debe crear una subclase que exponga el método. Esta técnica se emplea en SLF4J (Util#getCallingClass()).

public class A {
    public static void main(String[] args) {
        B.print();
    }
}

class B {
    public static void print() {
        System.out.println("Llamando a B#print");
        C.print();
    }
}

class C {
    public static void print() {
        ClassContextSecurityManager manager = getSecurityManager();
        if (manager != null) {
            System.out.println(Arrays.toString(manager.getClassContext()));
        }
    }

    static ClassContextSecurityManager getSecurityManager() {
        try {
            return new ClassContextSecurityManager();
        } catch (Throwable t) {
            return null;
        }
    }

    private static final class ClassContextSecurityManager extends SecurityManager {
        @Override
        protected Class<?>[] getClassContext() {
            return super.getClassContext();
        }
    }
}

Etiquetas: java StackTrace thread SecurityManager reflexión

Publicado el 8-2 11:26