Java获取调用当前方法的类名或方法名(栈堆信息)的四种方式举例

 更新时间:2024年09月01日 09:18:46   作者:星月昭铭  
在Java编程中我们经常需要在运行时获取当前执行的方法名称,这在日志记录、性能监控、调试等方面非常有用,这篇文章主要给大家介绍了关于Java获取调用当前方法的类名或方法名(栈堆信息)的四种方式,需要的朋友可以参考下

前言

在java代码中,是可以在运行时通过某种方式获取到当前方法被谁调用了(调用链路)。目前我所知道的有四种方式(通过Thread、Throwable、SecurityManager获取),下面逐个列出,附有代码和截图。

Thread.getAllStackTraces()方式

Thread.getAllStackTraces()可以获取所有线程的栈堆跟踪信息,返回值是Map<Thread, StackTraceElement[]>类型,可以通过Thread.currentThread()拿到当前Thread对象,从map集合中获取栈堆跟踪信息。StackTraceElement对象可以拿到调用者的类名、Class对象、调用的代码所在行数等。

代码如下:

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

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

class C {
    public static void print() {
        System.out.println("调用C#print");
        // 获取所有线程的栈堆跟踪集合
        Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
        // 去map集合里面拿当前线程的栈堆跟踪信息数组
        StackTraceElement[] stackTraceElements = allStackTraces.get(Thread.currentThread());
        System.out.println(Arrays.toString(stackTraceElements));
    }
}

可以看到,下标为4的StackTraceElement对象就是我们的main方法,之所以出现了下标0、1非自定义类的栈堆信息是因为调用了Thread.getAllStackTraces()方法,方法以及方法内部也执行了其他代码。

Thread.currentThread().getStackTrace()方式

通过Thread.currentThread()拿到当前的线程,再通过Thread对象的getStackTrace()方法获取线程的栈堆跟踪信息,getStackTrace()非static方法,所以要先拿到Thread对象。第一种和第二种方式实际上都是调用了private native static StackTraceElement[][] dumpThreads(Thread[] threads)本地方法来获取。这两种方式获取的话,栈堆跟踪信息数组里面都有非自定义类的调用信息(看下标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("调用B#print");
        C.print();
    }
}

class C {
    public static void print() {
        // 获取当前线程,再获取栈堆跟踪数组
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        System.out.println(Arrays.toString(stackTrace));
    }
}

new RuntimeException().getStackTrace()方式

实际上,只要是Throwable的子类包括他自身都可以,内部也是调用了一个本地方法来获取的栈堆信息。SpringApplication类中的deduceMainApplicationClass()方法就是通过这种方式来拿主类的Class对象。拿到了之后就可以通过for循环整个数组通过匹配方法名或者类型来拿想要信息了,或者直接通过整个数组拿到整个调用链路。

代码如下:

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

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

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

SecurityManager方式(这种可以拿到类信息,但是拿不到栈堆信息)

SecurityManager中有一个getClassContext()方法,这个方法可以获取到调用当前方法的Class信息,返回的类型是一个Class数组,和前面几种不同的是,这种方式没法获取代码行数、调用的方法等(Class对象大家应该很熟悉了,能拿到哪种信息应该都知道)。

由于getClassContext方法是非static的,所以要通过对象的方式调用,但是不能通过new SecurityManager()的对象来获取,因为这个方法是一个protected方法。protected native Class[] getClassContext()。所以需要自定义一个类,继承SecurityManager类,然后重写getClassContext方法,这样我们就能在这个类中使用这个方法了。

这种方式在slf4j中Util#getCallingClass()用到了

代码如下:

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

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

class C {

    public static void print() {
        // 获取一个安全管理器对象
        ClassContextSecurityManager securityManager = getSecurityManager();
        if (securityManager != null) {
            System.out.println(Arrays.toString(securityManager.getClassContext()));
        }
    }

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

    /**
     * 自定义的安全管理器,继承java.lang.SecurityManager类
     */
    private static final class ClassContextSecurityManager extends SecurityManager {
    	/**
    	 * 重写getClassContext方法,不然方法在SecurityManager中又被protected修饰,其他类还是调用不了
    	 */
    	@Override
        protected Class<?>[] getClassContext() {
            return super.getClassContext();
        }
    }
}

总结 

到此这篇关于Java获取调用当前方法的类名或方法名(栈堆信息)的四种方式的文章就介绍到这了,更多相关Java获取调用当前方法类名或方法名内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 教你如何轻松学会Java快慢指针法

    教你如何轻松学会Java快慢指针法

    要想把搬砖的效率提高,我们肯定是逃不掉数据结构和算法知识的,这不,可爱的小编今天就和大家一起学习来了,今天给大家分享的是快慢指针,那啥是快慢指针呢,文中有非常详细的解释,需要的朋友可以参考下
    2021-06-06
  • Eureka源码核心类预备知识

    Eureka源码核心类预备知识

    这篇文章主要为大家介绍了Eureka源码核心类预备知识详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Java模板动态生成word文件的方法步骤

    Java模板动态生成word文件的方法步骤

    最近项目中需要根据模板生成word文档,模板文件也是word文档。本文使用使用freemarker模板生成word文件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • 基于springboot+enum配置化的方法

    基于springboot+enum配置化的方法

    本文主要介绍利用Springboot结合枚举类enum进行自定义参数的初始化和应用,通过@Value注解实现参数的动态注入,以实现灵活可维护的配置管理,具有一定的参考价值,感兴趣的可以了解一下
    2024-09-09
  • SpringBoot项目中org.junit.jupiter.api.Test报错问题及解决

    SpringBoot项目中org.junit.jupiter.api.Test报错问题及解决

    这篇文章主要介绍了SpringBoot项目中org.junit.jupiter.api.Test报错问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Java比较器实现方法项目案例

    Java比较器实现方法项目案例

    这篇文章主要介绍了Java比较器实现方法,结合具体项目案例形式分析了Java比较器相关排序操作技巧,需要的朋友可以参考下
    2019-03-03
  • springboot上传图片文件步骤详解

    springboot上传图片文件步骤详解

    这篇文章主要介绍了springboot上传图片文件步骤详解,本文通过实例代码图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • MyBatis3用log4j在控制台输出SQL的方法示例

    MyBatis3用log4j在控制台输出SQL的方法示例

    本篇文章主要介绍了MyBatis3用log4j在控制台输出SQL的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • GitLab+Jenkins+Maven+Tomcat 实现自动集成、打包、部署

    GitLab+Jenkins+Maven+Tomcat 实现自动集成、打包、部署

    本文主要介绍了GitLab + Jenkins + Maven + Tomcat 实现自动集成、打包、部署,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Java中抽象类和接口的用法详解

    Java中抽象类和接口的用法详解

    这篇文章主要为大家详细介绍了Java中抽象类和接口的使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-08-08

最新评论