java-StackTraceElement获取类、方法执行调用栈的详细过程

一、什么是StackTrace

    StackTrace(堆栈轨迹)存放的就是方法调用栈的信息,每次调用一个方法会产生一个方法栈,当前方法调用另外一个方法时会使用栈将当前方法的现场信息保存在此方法栈当中,获取这个栈就可以得到方法调用的详细过程。例如:异常处理中常用的e.printStackTrace()实质就是打印异常调用的堆栈信息。

二、StackTraceElement介绍

    StackTraceElement表示StackTrace(堆栈轨迹)中的一个方法对象,通过这个对象可以获取调用栈当中的调用过程信息,包括方法的类名、方法名、文件名以及调用的行数。查看StackTraceElement​类的源代码,我们可以获取方法所在行、所在类等的信息;

/* @since  1.4
 * @author Josh Bloch
 */
public final class StackTraceElement implements java.io.Serializable {
    // Normally initialized by VM (public constructor added in 1.5)
    private String declaringClass;
    private String methodName;
    private String fileName;
    private int    lineNumber;

    public StackTraceElement(String declaringClass, String methodName,
                             String fileName, int lineNumber) {
        this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring class is null");
        this.methodName     = Objects.requireNonNull(methodName, "Method name is null");
        this.fileName       = fileName;
        this.lineNumber     = lineNumber;
    }
   
    public String getFileName() {
        return fileName;
    }

    public int getLineNumber() {
        return lineNumber;
    }

    public String getClassName() {
        return declaringClass;
    }
    
    public String getMethodName() {
        return methodName;
    }
    
    public boolean isNativeMethod() {
        return lineNumber == -2;
    }
    
    public String toString() {
        return getClassName() + "." + methodName +
            (isNativeMethod() ? "(Native Method)" :
             (fileName != null && lineNumber >= 0 ?
              "(" + fileName + ":" + lineNumber + ")" :
              (fileName != null ?  "("+fileName+")" : "(Unknown Source)")));
    }

    public boolean equals(Object obj) {
        if (obj==this)
            return true;
        if (!(obj instanceof StackTraceElement))
            return false;
        StackTraceElement e = (StackTraceElement)obj;
        return e.declaringClass.equals(declaringClass) &&
            e.lineNumber == lineNumber &&
            Objects.equals(methodName, e.methodName) &&
            Objects.equals(fileName, e.fileName);
    }
  
    public int hashCode() {
        int result = 31*declaringClass.hashCode() + methodName.hashCode();
        result = 31*result + Objects.hashCode(fileName);
        result = 31*result + lineNumber;
        return result;
    }
    
    private static final long serialVersionUID = 6992337162326171013L;
}

获取StackTraceElement的方法有两种,均返回StackTraceElement数组,也就是这个栈的信息:

///method 1
Thread.currentThread().getStackTrace()
///method 2
new Throwable().getStackTrace() 

测试代码和测试结果如下:

public class StackTraceElementTest {
    @Test
    public void testStackTraceElement(){
        获取堆栈轨迹数组
        StackTraceElement[] element = Thread.currentThread().getStackTrace();
        for (int i = 0 ;i < element.length; i++){
            System.out.println(element[i]);
        }
    }
}
执行结果如下:
cn.missbe.util.StackTraceElementTest.testStackTraceElement(StackTraceElementTest.java:21)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
org.junit.runners.ParentRunner.run(ParentRunner.java:309)
org.junit.runner.JUnitCore.run(JUnitCore.java:160)
com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

从上面的测试结果可以看出,开始执行点为JUnitStarter.java:70由Junit测试框架进行调用,我们可以看到Junit的详细调用过程,可以获取整个方法调用栈信息;

三、用途

1、进行源码分析,通过StackTraceElement对象,可以知道框架的调用过程,帮助我们理解执行流程。例如上面的Junit测试框架,我们可以根据堆栈信息知道Junit的调用过程;

2、我们可以进行包装日志类,如log4j,在打印目标日志信息的时候,可以通过StackTraceElement​ 对象获取调用的方法,方法所在的类,方法所在行数写入日志文件当中,代码如下:

StackTraceElement[] temp = Thread.currentThread().getStackTrace();
///最近的调用方法
StackTraceElement method = temp[1];
for (StackTraceElement stackTraceElement : temp) {
    if (stackTraceElement.getClassName().contains("police")
                    && !stackTraceElement.getClassName().contains("CGLIB")
                    && !stackTraceElement.getMethodName().equals("print")
                    && !stackTraceElement.getMethodName().equals("around")) {
        method = stackTraceElement;
        break;
    }
 }
String fromLine = method.getClassName().replaceAll(App.PACKAGE_BASE + ".", "") + "." + method.getMethodName()+ "() line@" + method.getLineNumber();
logMsgStr写入日志文件或者输出到控制台
String logMsgStr = sdf.format(Calendar.getInstance().getTime()) + " :" + msgStr + " @来自:" + fromLine;

 

欢迎关注南阁公众号


南阁

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值